每日一题36. 小AA的数列 (二进制DP)
Posted tags: 篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一题36. 小AA的数列 (二进制DP)相关的知识,希望对你有一定的参考价值。 补题链接:Here 算法涉及:位运算,DP 这道题想了很久但实在没想什么巧妙的解法,暴力的代码就不放,这里引用Kur1su 的思路 异或问题优先考虑二进制位,对于这个问题,我们需要考虑偶数长度的区间,那么先对 \\([L, R]\\) 做处理,因为如果 \\(L,R\\) 是奇数其实加一/减一没有区别。然后处理一下前缀异或和, 因为我们有性质 \\(sum[l, r] = sum[r] \\oplus sum[l - 1]\\)。最后我们要找到哪些是有贡献的,我们考虑枚举右端点,如果当前点的异或是 1,那么需要在前面找异或为 0 的点才有贡献,反之如果当前点的异或是 0,那么要在前面找异或为 1 的点才有贡献,此外,奇数下标要找奇数下标,偶数下标要找偶数下标,才能构成偶数长度区间。 以上是关于每日一题36. 小AA的数列 (二进制DP)的主要内容,如果未能解决你的问题,请参考以下文章
所以我们可以用 \\(dp[i][j]\\) 维护前面符合条件的状态数,第一维表示当前位为 \\(0/1,\\) 第二维表示当前下标为 奇/偶的状态数,直接计算贡献即可。using ll = long long;
const int N = 1e6 + 10, mod = 1e9 + 7;
int a[N];
void solve() {
int n, l, r; cin >> n >> l >> r;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
a[i] ^= a[i - 1];
}
if (l & 1) l++; // 保证区间为偶数长度
if (r & 1) r--; // 保证区间为偶数长度
if (l > r) { cout << 0; return ;}
ll ans = 0;
for (int i = 0; i < 32; ++i) { // 二进制运算
ll p = (1ll << i);
ll dp[2][2] = {0};
ll num = 0;
for (int j = l; j <= n; ++j) {
dp[(a[j - l] >> i) & 1][(j - l) & 1]++;
num += dp[((a[j] >> i) & 1) ^ 1][j & 1];
num %= mod;
if (j >= r)dp[(a[j - r] >> i) & 1][(j - r) & 1]--;
}
ans += num * p % mod;
ans %= mod;
}
cout << ans;
}