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;
}
posted @ 2020-12-04 13:23  b1ts  阅读(89)  评论(0编辑  收藏  举报