Description
思路
由题目条件 b i & b i − 1 ≠ 0 b_i \&b_{i-1} \neq 0 bi&bi−1=0,可以得到:只要两个数的二进制展开中,在任意一位相同的二进制位上,两个数都为1,那么与运算之后结果就一定不为0。
考虑到 n ≤ 100000 n\leq100000 n≤100000,无法使用类似常规的最长不降子序列的做法,我们可以利用位运算的特点——不同二进制位之间的位运算相互独立,来将各个二进制位分开进行统计以此加快效率,这也是位运算题目中经常使用的技巧。
例如本题中,设 f i , j f_{i,j} fi,j表示,当前正在处理的数应该往前连接上的那个数,在其第 j j j位为1的情况下,前 i i i个数所能达到的最大长度。
那么就只需将当前数(为第 i i i个数,代码中为 k k k)中为1的每个二进制位所对应的 f i − 1 , j f_{i-1,j} fi−1,j加1后进行比较,取其中的最大值,最后赋值回去到所有 f i , j f_{i,j} fi,j中即可(因为如果k的第 j j j位为1,能与之前的数连接上,那么就相当于 k k k的所有为1的位都连接上了。所以只需要取其中的最大值)。
代码
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int N=1e5;
int n,ans,f[N+1][32];
int main()
{
scanf("%d",&n);
for(int i=1,k;i<=n;i++)
{
int len=0;
scanf("%d",&k);
for(int j=0;j<32;j++)
if(k&(1<<j)) len=max(len,f[i-1][j]+1);
for(int j=0;j<32;j++)
if(k&(1<<j)) f[i][j]=len;
else f[i][j]=f[i-1][j];
}
for(int i=0;i<32;i++)
ans=max(f[n][i],ans);
printf("%d",ans);
return 0;
}