1. 程式人生 > 實用技巧 >luoguP4310 絕世好題

luoguP4310 絕世好題

題麵點這裡

題面簡述:

給定一個長度為 $ n $ 的數列 $ a_i $ ,求 $ a_i $ 的一個最長子序列 $ b_i $ (假設長度為 $ m $ ),滿足 $ b_{i} $ & $ b_{i+1} ≠ 0 ( 1 \leq i < k ) $ 。

資料範圍:$ 1 \leq n \leq 10^5 , 1 \leq a_i \leq 10^9 $ 。

題解:

有一個樸素的想法就是設 $ f_i $ 表示當前序列最後一個為 $ a_i $ 的長度的最大值,顯然這樣直接轉移是 $ O(n^2) $ 的。

考慮如何優化這個東西。注意到 $ b_{i} $ & $ b_{i+1} ≠ 0 $ 的條件就是它們兩個數在二進位制表示下在某一位上同為 $ 1 $ 。於是設 $ g_j $ 表示在第 $ j $ 位上為 $ 1 $ 的序列長度的最大值,列舉到每一位上轉移即可。時間複雜度 $ O( n log \max {a_i}) $ 。

#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int f=1,r=0;char c=getchar();
	while(!isdigit(c))f^=c=='-',c=getchar();
	while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
	return f?r:-r;
}
const int N=1e5+5;
int n,ans,g[33];
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		int x=read(),f=0;
		for(int j=30;~j;j--)
			if(x&(1<<j))
				f=max(f,g[j]+1);
		for(int j=30;~j;j--)//由於上面是取最大值,所以這裡就不要再去取最大值了。
			if(x&(1<<j))g[j]=f;
		ans=max(ans,f);
	}
	return cout<<ans,0;
}