题目链接:https://projecteuler.net/problem=409
题目:
题解:
题目问你必胜态的数目,我们考虑用总的方案数减去必败态的方案数(NIM游戏没有平局这个操作)
必败态的方案数就是让我们构造一个长度为n的数列,要求没有重复的数,每一数的值在[1,$2^n-1$]之间,且所有数异或和为0(这个就不需要证明了吧,不过我以前的一篇取石子的博客里好像有证明)
设$f[n]$表示长度为$n$的这样的数列的方案数
显然$f[1]=f[2]=0$
考虑对于$f[i]$怎么转移,设$m=i-1$,$f[i]=(2^i-1)*(2^i-2)*...*(2^i-m)-f[i-1]-f[i-2]*(i-1)*(2^i-m)$
为什么呢?我们知道在已经确定前n-1个数的情况下,第n个数的取值一定是固定的,前n-1个数不相等的方案数是$(2^i-1)*(2^i-2)*...*(2^i-m)$
现在显然是有重复的情况,第一种,第$i$个数等于$0$,我们在总方案数里减去$f[i-1]$
第二种,第$i$个数在之前出现过了,总方案数减去$f[i-2]*(i-1)*(2^i-m)$
主要对第二种的减掉的方案数进行说明,我们其实现在减掉的方案数就是构建一个长度为$i$的,最后一位和之前有重复的数列,考虑我们先得到$i-2$个元素的异或和为$0$且元素互不重复的数列,然后我们在这个数列的基础上加上两个一样的数。因为总方案数前$i-1$个数不重复,这个一样的数显然有$(2^i-m)$种取值,然后我们还要考虑一下两个一样的数除了一个在最后一个位置外另一个在的位置,位置有$i-1$种取法($i-2+1=i-1$)
标程如下:
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; typedef long long ll; const ll N=1e7+15; const ll mod=1e9+7; ll n,s,now; ll f[N]; ll qpow(ll a,ll b,ll mo) { ll res=1; for (;b;a=a*a%mo,b>>=1) if (b&1) res=res*a%mo; return res; } int main() { n=1e7; s=qpow(2,n,mod); f[1]=0;f[2]=0; now=s-1; for (int i=3;i<=n;i++) { int m=i-1; now=now*(s-m+mod)%mod; f[i]=now; f[i]=(f[i]-f[i-1])%mod; f[i]=(f[i]-f[i-2]*(i-1)%mod*(s-m)%mod)%mod; //printf("%lld %lld %lld ",now,f[i-1],f[i-2]*(i-1)*(s-m+1)%mod); //printf("%d %lld ",i,f[i]); } printf("%lld ",(now*(s-n)%mod-f[n]%mod)%mod); return 0; }