首先考虑暴力的转移,当枚举到第 iii 个数时,我们尝试将之前所有的有效状态的 P,Q,RP,Q,RP,Q,R 均与 aia_iai 尝试去异或,然后检测合法性。于是就有:
la[{0,0,0}] = 1;
for (int i = 1;i <= n;++i)
{
for (auto item : la)
{
node v = item.first;
int x = v.p,y = v.q,z = v.r;
if (y == z || (x ^ a[i]) == y || (x ^ a[i]) == z) dp[{x ^ a[i],y,z}] += la[v];
if (x == z || (y ^ a[i]) == x || (y ^ a[i]) == z) dp[{x,y ^ a[i],z}] += la[v];
if (x == y || (z ^ a[i]) == x || (z ^ a[i]) == y) dp[{x,y,z ^ a[i]}] += la[v];
}
for (auto item : dp) dp[item.first] %= MOD;
la.clear ();la = dp;dp.clear ();
}
但这样显然会有很多冗余的转移以及判断,考虑异或的性质去优化。
回顾一下题面,对于一个数字只能选择 P,Q,RP,Q,RP,Q,R 中的一个去异或,那么处理完 aia_iai 后,P⊕Q⊕R=⊕j=1iaj=piP \oplus Q \oplus R = \oplus _{j = 1}^i a_j = p_iP⊕Q⊕R=⊕j=1iaj=pi。由于 P,Q,RP,Q,RP,Q,R 不能两两互异,此时设 (P,Q,R)(P,Q,R)(P,Q,R) 中相同的数为 xxx,由于 x⊕y⊕y=xx \oplus y \oplus y = xx⊕y⊕y=x,故可能的有效状态为 (x,x,pi),(x,pi,x),(pi,x,x)(x,x,p_i),(x,p_i,x),(p_i,x,x)(x,x,pi),(x,pi,x),(pi,x,x) 中的一个。
设 dpi,xdp_{i,x}dpi,x 表示在处理第 iii 个数后相同的数为 xxx 的方案数。则 (x,x,pi)(x,x,p_i)(x,x,pi) 的上一个状态可能为 (x⊕ai,x,pi)(x \oplus a_i,x,p_i)(x⊕ai,x,pi) 或 (x,x,pi⊕ai)(x,x,p_i \oplus a_i)(x,x,pi⊕ai)。
-
上一个状态为 (x⊕ai,x,pi)(x \oplus a_i,x,p_i)(x⊕ai,x,pi)
由于要保证状态的合法性,需要分类讨论具体是哪两个数相同:
- x⊕ai=x⇒ai=0x \oplus a_i = x \Rightarrow a_i = 0x⊕ai=x⇒ai=0,与题干矛盾,故不可能成立。
- x⊕ai=pi⇒x=pi−1x \oplus a_i = p_i \Rightarrow x = p_{i - 1}x⊕ai=pi⇒x=pi−1,此时转移方程为 dpi,x=dpi,pi−1=dpi−1,pidp_{i,x} = dp_{i,p_{i - 1}} = dp_{i-1,p_i}dpi,x=dpi,pi−1=dpi−1,pi。
- x=pix = p_ix=pi,此时转移方程为 dpi,x=dpi,pi=dpi−1,pidp_{i,x} = dp_{i,p_i} = dp_{i - 1,p_i}dpi,x=dpi,pi=dpi−1,pi
-
上一个状态为 (x,x,pi⊕ai)(x,x,p_i \oplus a_i)(x,x,pi⊕ai)
显然,转移方程为 dpi,x=dpi−1,xdp_{i,x} = dp_{i - 1,x}dpi,x=dpi−1,x。
通过观察可以发现,dpi,x←dpi−1,xdp_{i,x} \leftarrow dp_{i - 1,x}dpi,x←dpi−1,x 不成立当且仅当 x=pi−1x = p_{i - 1}x=pi−1 的情况,其余直接继承。最终状态为 (x,x,pi)(x,x,p_i)(x,x,pi),代入 xxx 后是 (pi−1,pi−1,pi)(p_{i - 1},p_{i - 1},p_i)(pi−1,pi−1,pi)。由 (pi−1,pi−1,pi−1)(p_{i - 1},p_{i - 1},p_{i - 1})(pi−1,pi−1,pi−1) 转移共有三种方式,而由 (pi−1,pi,pi)(p_{i - 1},p_i,p_i)(pi−1,pi,pi) 转移共有两种方式。同时,可以发现可以利用滚动数组优化,因此可以有:
dppi−1=2×dppi+3×dppi−1 dp_{p_{i - 1}} = 2 \times dp_{p_i} + 3 \times dp_{p_{i - 1}} dppi−1=2×dppi+3×dppi−1
当然,初始条件为 dp0=1dp_{0} = 1dp0=1。数字过大,离散化或者直接用 map 即可。代码如下:
#include <bits/stdc++.h>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 2e5 + 5;
const int MOD = 1e9 + 7;
inline int read ();
int t,n,a[MAX],p[MAX];ll ans;
map <int,ll> dp;
int main ()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
t = read ();
while (t--)
{
n = read ();ans = 0;dp.clear ();
for (int i = 1;i <= n;++i) a[i] = read (),p[i] = p[i - 1] ^ a[i];
dp[0] = 1;
for (int i = 1;i <= n;++i) dp[p[i - 1]] = (dp[p[i]] * 2 + dp[p[i - 1]] * 3) % MOD;
for (auto item : dp) ans = (ans + item.second) % MOD;
printf ("%lld\n",ans);
}
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}
1177

被折叠的 条评论
为什么被折叠?



