最長

cn是大帅哥886發表於2024-08-13
https://www.luogu.com.cn/problem/P4310

給定一個長度是n的序列a[1,2,...n],現在你需要構造一個長度為m的陣列b[1,2,...m],需要滿足如下條件:

(1)b是a的子序列

(2)b[i]&b[i-1]!=0 (2<=i<=m)

問構造出b後,m的最大值是多少。

輸入格式

輸入檔案共2行。第一行包括一個整數n。

第二行包括n個整數,第i個整數表示a[i]。

1<=n<=1e5,1<=a[i]<=1e9

輸出格式

一個整數,表示m的最大值

輸入/輸出例子1

輸入:

3

1 2 3

輸出:

2

樣例解釋

位運算都考慮拆位!一位一位看!

做法一:
f[i]:以第i個數結尾滿足的最長序列
記錄lst陣列,第i進位制位是1屬於的最近的a陣列下標

https://www.luogu.com.cn/article/ncpi87q5

做法二:
逐步考慮每一位
對於每一位,二進位制位數上是1的可以建一個圖,例如

a的第3個進位制位是1
b的第3個進位制位是1
那麼我們建 a->b 的有向邊

圖保證無環,我們找圖的最長鏈就是答案(拓撲)

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;

int n, b[N], d[N], ans=0, dis[N];
vector<int> a[N];
queue<int> q;
void topsort()
{
	q.push(0);
	
	while (!q.empty())
	{
		int u=q.front();
		q.pop();
		
		for (int i=0; i<a[u].size(); i++)
		{
			int v=a[u][i];
			
			if (dis[v]<dis[u]+1) dis[v]=dis[u]+1;
			ans=max(ans, dis[v]);
			
			d[v]--;
			if (!d[v]) q.push(v);
		}
	}
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", &b[i]);
	
	for (int k=0; k<=32; k++)
	{
		int last=0;
		for (int i=1; i<=n; i++)
			if (b[i]&(1<<k))
			{
				a[last].push_back(i);
				d[i]++;
				last=i;
			}
	}
		
	topsort();
	
	printf("%d", ans);
	return 0;
}

  


做法三:
覆蓋

https://www.luogu.com.cn/article/w99xswjw

#include <bits/stdc++.h>
using namespace std;
const int N=50;

int n, a, f[N], ans=0;
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) 
	{
		int t=0, Max=0, cnt=0;
		scanf("%d", &a);
		
		t=a;
		while (t>0)
		{
			cnt++;
			if (t&1) Max=max(Max, f[cnt]);
			t>>=1;
		}
		
		cnt=0, t=a;
		while (t>0)
		{
			cnt++;
			if (t&1) f[cnt]=max(Max+1, f[cnt]);
			t>>=1;
		}
	}
	
	for (int i=1; i<=32; i++) ans=max(ans, f[i]);
	
	printf("%d", ans);
	return 0;
}

  

相關文章