CF2027E2

显然独立求出每堆的 SG(a,x) 即可。

什么时候无法下棋?当且仅当不存在一个x 的子集满足其 ai,也就是 lowbit(x)>ai

但是同时,比 ai 更大的二进制位是无法动弹的,可以直接删去

所以这时候我们就保证了末状态一定是零。

同时对于 a,x 二进制下同为零的位也可以删去,这样我们保证了 a|x=2k1

同时我们找到最大的 a1x0 的二进制位,这说明比它更低的位的 a,可以变成全是 1 的,同时缩掉这一位。

如果不存在,那么说明 ax 的子集,那么 x=2k1,同时我们知道 a,x​ 最高位相同,所以这个时刻,我们就可以直接用 a 来表达 x

通过反复进行调整,可以发现最开始的最大的 a1x0 的二进制位往下,x 所有为零的位都会被缩掉,因此通过此操作,我们可以将局面间接转化为:(a,2log2a+11)

  1. 删掉比 highbit(a) 更大的二进制位
  2. 找到 maxt(a)t=1,(x)t=0,将 tx 的零位全部删掉,并且将 a 对应的位变成 1

考虑这样的一个 asg 函数值。

它能够转移到什么样的状态?

显然 sg 值与二进制下最高位有关系,不妨打表,可以发现

  1. sg(2k1)=k
  2. sg(2k2)=0,k1
  3. sg(2k)=k1
  4. 其余值都是 highbit(x)+1

若SG函数打表无法看出明显规律,不妨先想办法划分等效状态,这可以简化找到规律的难度


发现 sg 值全是 log 级别,我们做 E2 就是只需要算出每个 sg 值被取出的次数

如何优化我们的调整,亦或者用更简洁的语言表达?

首先是 x&=2highbit(a)+11

如果我们先不删掉同为零的位,而是放任不管,那么这个位数是 popcount((2highbit(a)+11)(x|a))

然后至于最大的 a1x 为零,应当是 rev(x)&a 的最高位吧。

接着我们进行的变化是考虑这个位往下的所有 x0 位,这是需要删掉的

往上的位不变。

比较麻烦啊,能不能直接算

考虑数位 dp,发现贡献只需要考虑 2k,2k1,2k2 和其他值,不妨设 dpdep,flg,tag,j,k,d 表示已经填了二进制下不低于 dep 的位,此时有无最高位限制,是否已经填过一个 a1x 取零的二进制位,当前的 a 二进制下共出现过 j1,当前的 x 的位数已经确定了 k,同时 2ka=d

因为记录差不容易处理 2t 的情形,所以额外记录了一个位数,因此有 j 只需要记录到 2d 只需要记录到 3 即可。

初始化 dp31,1,0,0,0,1=1 ,分类讨论进行转移。

void dp(){
	memset(f,0,sizeof f);
	f[31][1][0][0][0][1]=1;
	for(int now=31;now;--now)for(int flg:{0,1})for(int ap:{0,1})
	for(int bit:{0,1,2})for(int c=0;c+now<=31;++c)for(int d:{0,1,2,3}){
		int v=f[now][flg][ap][bit][c][d];
		if(!v)continue;
		int ok=(now-1<=ha);
		int va=(A>>now-1)&1,vb=(B>>now-1)&1;
		if(!ok){
			f[now-1][flg&(vb==0)][ap][bit][c][d]+=v;
			if(!flg||vb)f[now-1][flg][ap][bit][c][d]+=v;
			continue;
		}
		int up=flg?vb:1;
		for(int i=0;i<=up;++i){
			if(!va&&!i)f[now-1][flg&(vb==i)][ap][bit][c][d]+=v;
			else if(!va&&i){
				if(ap)f[now-1][flg&(vb==i)][ap][min(bit+1,2ll)][c+1][min(3ll,max(0ll,(d<<1)-1))]+=v;
				else f[now-1][flg&(vb==i)][ap][bit][c+1][min(3ll,d<<1)]+=v;
			}
			else if(va&&!i)f[now-1][flg&(vb==i)][1][bit][c][d]+=v;
			else f[now-1][flg&(vb==i)][ap][min(2ll,bit+1)][c+1][min(3ll,max(0ll,(d<<1)-1))]+=v;
		}
	}
	for(int flg:{0,1})for(int ap:{0,1})for(auto bit:{0,1,2})for(int c=0;c<=31;++c)for(int d=0;d<4;++d)if(f[0][flg][ap][bit][c][d]){
		int sg=0;
		if(d==0)sg=c^1;
		else if(d==2)sg=0;
		else sg=c;
		if(!c)sg=0;
		if(bit==0)sg=0;
		else if(bit==1&&c)sg=(c-1)^1;
		g[sg]+=f[0][flg][ap][bit][c][d];
	}
	g[0]--;
}
posted @   spdarkle  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示