Luogu P4310 绝世好题 题解 [ 绿 ] [ 线性 dp ] [ 单调队列优化 ] [ 二进制优化 ]

题目:绝世好题

暴力 dp

显然 \(O(n^2)\) 转移即可。

单调队列优化

观察到只有某二进制位两个数都为 \(1\) 时才能转移,因此我们把每个二进制位开一个单调队列,然后对于一个数 \(a\) ,找到它是 \(1\) 的二进制位并选其单调队列的队头进行转移,在这之后把这个数加入符合要求的单调队列中即可。

因为我懒 因为没有弹出元素操作并且多个 log 无伤大雅,所以单调队列可以用优先队列代替。

单调队列的时间是 \(O(n \log n)\),要是懒的话用优先队列 \(O(n \log n \log n)\) 依然可以过。

代码就不写了。

二进制优化

定义 \(f[i]\) 为在二进制下以第 \(i\) 位为1结尾的最长序列长度。

我们在 dp 时,先找出 \(a\) 哪一个二进制位为 \(1\) ,就说明这一位为结尾的可以转移过来,记录下转移最大值。记录之后再找出 \(a\) 哪一个二进制位为 \(1\) ,把转移的最大值继续转移下去即可。

#include <bits/stdc++.h>
using namespace std;
int n,a,f[40],ans=0;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a;
		int tmp=0;
		for(int j=0;j<=32;j++)
		{
			if((a>>j)&1)
			{
				tmp=max(tmp,f[j]);
			}
		}
		for(int j=0;j<=32;j++)
		{
			if((a>>j)&1)
			{
				f[j]=max(f[j],tmp+1);
			}
			ans=max(ans,f[j]);
		}		
	}
	cout<<ans;
	return 0;
}

时间 \(O(n \log n)\)

本质是滚动数组充当了我们单调队列的作用,因为它会存下某一位结尾的最大长度,而不用我们手动去找,并且还省空间。

posted @ 2024-07-22 21:14  KS_Fszha  阅读(2)  评论(0编辑  收藏  举报