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;
}