题解:CF2066C Bitwise Slides

首先考虑暴力的转移,当枚举到第 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_iPQR=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 = xxyy=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)(xai,x,pi)(x,x,pi⊕ai)(x,x,p_i \oplus a_i)(x,x,piai)

  • 上一个状态为 (x⊕ai,x,pi)(x \oplus a_i,x,p_i)(xai,x,pi)

    由于要保证状态的合法性,需要分类讨论具体是哪两个数相同:

    1. x⊕ai=x⇒ai=0x \oplus a_i = x \Rightarrow a_i = 0xai=xai=0,与题干矛盾,故不可能成立。
    2. x⊕ai=pi⇒x=pi−1x \oplus a_i = p_i \Rightarrow x = p_{i - 1}xai=pix=pi1,此时转移方程为 dpi,x=dpi,pi−1=dpi−1,pidp_{i,x} = dp_{i,p_{i - 1}} = dp_{i-1,p_i}dpi,x=dpi,pi1=dpi1,pi
    3. 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=dpi1,pi
  • 上一个状态为 (x,x,pi⊕ai)(x,x,p_i \oplus a_i)(x,x,piai)

    显然,转移方程为 dpi,x=dpi−1,xdp_{i,x} = dp_{i - 1,x}dpi,x=dpi1,x

通过观察可以发现,dpi,x←dpi−1,xdp_{i,x} \leftarrow dp_{i - 1,x}dpi,xdpi1,x 不成立当且仅当 x=pi−1x = p_{i - 1}x=pi1 的情况,其余直接继承。最终状态为 (x,x,pi)(x,x,p_i)(x,x,pi),代入 xxx 后是 (pi−1,pi−1,pi)(p_{i - 1},p_{i - 1},p_i)(pi1,pi1,pi)。由 (pi−1,pi−1,pi−1)(p_{i - 1},p_{i - 1},p_{i - 1})(pi1,pi1,pi1) 转移共有三种方式,而由 (pi−1,pi,pi)(p_{i - 1},p_i,p_i)(pi1,pi,pi) 转移共有两种方式。同时,可以发现可以利用滚动数组优化,因此可以有:

dppi−1=2×dppi+3×dppi−1 dp_{p_{i - 1}} = 2 \times dp_{p_i} + 3 \times dp_{p_{i - 1}} dppi1=2×dppi+3×dppi1

当然,初始条件为 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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值