洛谷 P4310 绝世好题 题解

洛谷 P4310 绝世好题 题解

xiaoh 2020-7-22

题意

给定nn个正整数aia_i,求aa最长的满足bi&bi+10b_i\&b_{i+1}\ne0子序列bb的长度。

数据范围

1n105,0ai1091\leq n\leq 10^5,0\leq a_i\leq 10^9

题解

首先考虑最朴素的dpdp方程。记fif_i为以aia_i为结尾的最长的bb。那么转移方程为:

fi=[1j<i,ai&aj0]  max(aj+1)f_i=[1\leq j<i,a_i\&a_j\ne 0]\;\max (a_j+1)

但是这个转移方程是O(n2)O(n^2)的,那么我们如何进行优化呢?我们想到了坐标转换。既然要求bi&bi+10b_i\& b_{i+1}\ne 0,那么我们不妨设计一个dpdp方程使其在dpdp过程中始终满足这个性质。我们令fif_ibb序列最后一个元素二进制第ii位为11的最长的子序列,那么此时转移方程为:

fi=[a[j]&(1<<i)0,a[j]&(1<<k)0]  max(fk+1)f_i=[a[j]\&(1<<i)\ne 0,a[j]\&(1<<k)\ne 0]\;max(f_k+1)

此时复杂度就变为了O(nlogai)O(n\log a_i),直接计算即可

Code

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T &x)
{
	x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
}
	while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
	x*=f;
	return;
}	
template<typename T>
void write(T x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10);
	putchar(x%10+'0');
	return;
}
const int MAXN=100010,LOGN=31;
int n;
int a[MAXN];
int f[MAXN];
int ans=0;
int main()
{
    read(n);
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=1;i<=n;++i)
    {
        int maxn=0;
        for(int j=0;j<LOGN;++j)
        if(a[i]&(1<<j)) maxn=max(maxn,f[j]);
        for(int j=0;j<LOGN;++j)
        if(a[i]&(1<<j)) f[j]=max(f[j],maxn+1);
    }
    for(int i=0;i<LOGN;++i) ans=max(ans,f[i]);
    write(ans),putchar('\n');
	return 0;
}