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;
}
浙公网安备 33010602011771号