题解:CF2067F Bitwise Slides

Statement

给定长度为 \(n\) 的数组 \(a\),同时你拥有一个三元组 \((P,Q,R)\),每次定义操作如下:

  1. \(P:=P \oplus a_i\)

  2. \(Q:=Q \oplus a_i\)

  3. \(R:=R \oplus a_i\)

显然的,最多有 \(3^n\) 次操作方法。

我们定义规则:每次操作后必须保证三元组内有两数相等。

求有多少种操作方法不违背规则,答案对 \(10^9 + 7\) 取模。

\(n \leq 2 \times 10^5\)

Solution

异或有一个性质:\(x \oplus x = 0\),可以从这里入手。

我们记 \(p_i\)\(a\) 数组的前缀异或和,我们会发现每次操作后 \(P \oplus Q \oplus R = p_i\),所以说每次三元组的状态就是:

\[(p_i,x,x) \\ (x,p_i,x) \\ (x,x,p_i) \]

考虑它们从何转移而来,记 \(dp_{i,x}\) 表示当前在 \(i\) 三元组中相等两数值为 \(x\) 的方案数。

首先对于 \((p_i,x,x)\),它是从 \((p_i \oplus a_i,x,x)\) 转移而来,根据异或的性质,\(p_i \oplus a_i = p_{i-1}\),所以这里 \(dp_{i,x} = dp_{i-1,x}\)

对于另外两种,当 \(x = p_i\) 时,状态为 \((p_i,p_i,p_i)\),它可以从 \((p_{i-1},p_i,p_i),(p_i,p_{i-1},p_i),(p_i,p_i,p_{i-1})\) 任意一种转移过来,所以 \(dp_{i,x} = dp_{i-1,x}\),当 \(x = p_{i-1}\) 时,它可以从 \((p_{i-1},p_{i-1},p_{i-1})\) 转移,这样有三种方案,也可以从 \((p_{i-1},p_i,p_i),(p_i,p_{i-1},p_i),(p_i,p_i,p_{i-1})\) 共两种方案,注意到都只和 \(i-1\) 有关,滚动数组优化,所以这样一番推导下方程就有了:

\[dp_{p_{i-1}} = dp_{p_{i-1}} \times 3 + dp_{p_i} \times 2 \]

最后答案即为 \(\sum\limits_{i=1}^{n}{dp_{p_i}}\)

数组开不下用 map 映射。

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e5 + 10, MOD = 1e9 + 7;
int T, n, A[MAXN], preXor[MAXN];
map <int, int> dp;

inline void Dp() {
    cin >> n, dp.clear();
    for (int i = 1; i <= n; i ++)
        cin >> A[i], preXor[i] = preXor[i - 1] ^ A[i];
    dp[0] = 1;
    for (int i = 1; i <= n; i ++) 
        dp[preXor[i - 1]] = (dp[preXor[i - 1]] * 3 % MOD + dp[preXor[i]] * 2 % MOD) % MOD;
    int Ans = 0;
    for (auto [u, v] : dp)
        Ans = (Ans + v) % MOD;
    cout << Ans << '\n';
}

signed main() {
    cin.tie (0) -> sync_with_stdio (0);
    cout.tie (0) -> sync_with_stdio (0);
    cin >> T;
    while (T --) Dp();
    return 0;
}
posted @ 2025-02-12 18:45  xAlec  阅读(91)  评论(0)    收藏  举报