P4310 绝世好题 题解

P4310 绝世好题 题解

简化题意

有一个长度为 \(n\) 的数列 \(a\),求 \(a\) 的子序列 \(b\) 的最长长度 \(k\),满足 $b_i \land b_{i-1} \ne 0 $,其中 \(2\leq i\leq k\)
(原题已经很简洁了,就不简化了)

解题思路

首先,有一个很简单的 DP 思想,与最长递增子序列那题差不多,就是若有 \(a_i \land a_j \ne 0\)\(1 \leq j < i\)),则有 \(dp_i = \max \{ dp_i, dp_j + 1 \}\)
这样显然是过不了的,因为时间复杂度要到 \(O(n ^ 2)\),而 \(n\) 最大为 \(10 ^ 5\)

其实正赛要是我能做到这一步,想不出下一步就可以跳过了

正解思路其实是位运算。因为我们可以发现一点:就是与运算的规则是两位同时为 \(1\),结果才为 \(1\),否则为 \(0\)
那么我们创建一个数组 \(dp\),其中 \(dp_i\) 代表二进制表示下,当前最后一个数第 \(i\) 位的最长子序列长度。
不难发现,若一个数的一位为 \(1\),那么这位就可以考虑加入子序列,长度加一。
最后对于每位为 \(1\) 的位置 \(i\),执行 \(dp_i\) = \(\max \{dp_i \} + 1\)(因为同一个数内,可以相互转移)。

代码部分

#include <bits/stdc++.h>
using namespace std;

int n;
int dp[40];

int main()
{
	cin >> n;
	for(int i = 1, a;i <= n;i ++)
	{
		cin >> a;
		int maxn = 0;
		for(int i = 0;(1 << i) <= a;i ++)
			if(a & (1 << i))
				maxn = max(maxn, dp[i] + 1);
		
		for(int i = 0;(1 << i) <= a;i ++)
			if(a & (1 << i))
				dp[i] = maxn;
	}
	
	int ans = 0;
	for(int i = 0;i < 32;i ++)
		ans = max(ans, dp[i]);
	
	
	cout << ans << endl;
	return 0;
}

posted on 2026-09-01 22:16  Leo_29  阅读(5)  评论(0)    收藏  举报

导航