联考20200729 T2 划愤
分析:
(为什么我这种准退役选手要学这种神仙东西啊2333
考场上直接暴力,暴力都拿不到分。。。
我们先考虑这样一个问题:
n*m的格子上亮着一些灯,两人轮流进行如下操作:
选择一个矩形满足右下角的灯是亮的,将四个角的灯改变状态,无法操作者负
如果这个游戏是一维的,我们选一个亮着的灯的位置\(n\)和前面一个灯翻转,相当于\(n\)个石子里取走一部分
是一个简单的Nim游戏,初始状态所有亮着的灯,他们分别会代表一个子游戏各自独立
现在游戏变成二维,一个位置与某三个位置翻转,这三个位置分别形成三个子游戏
于是数学家们便引入了Nim积,能够很好地表达上面的游戏中每个点的SG计算方式,并从中发掘了一些数学性质使我们加速运算
直接膜拜大佬博客
(相信大家已经学会了\(O(log^2n)\)求某个位置的Nim积了2333
掌握了Nim积的知识回来看这道题
相当于游戏变成\(N\)维,一个位置与某\(2^N-1\)个位置翻转,分别形成\(2^N-1\)个子游戏
二维Nim积\(x\otimes y\)拓展到\(N\)维就是\(a_1\otimes a_2\otimes ...\otimes a_N\)
单个游戏的我们会求了,但是这里有\(N!\)个游戏,暴力不可接受
看看这\(N!\)个游戏的形成方式,感觉跟行列式非常的像
我们知道Nim积和异或运算满足交换律,结合律,分配率
我们把行列式的加法重载为异或,乘法重载为Nim积,发现不会出现错误
至于行列式里面的\(-1\),由于异或拥有很好地数学性质,其逆运算是他本身,\(-1\)直接可以忽略不计
直接对原矩阵求重载运算后的行列式,就可以得到最终的SG了,不为0则先手胜
现在还有最后一个问题,关于Nim积的逆元
这个我不是很懂,我直接与质数取模乘法类比的(
Nim积运算集合中单位元是1,乘法运算集合中单位元也是1
当\(x,y<2^{2^k}\)时,\(x\otimes y<2^{2^k}\)
相当于\([0,2^{2^k})\)中的数Nim积后结果仍在\([0,2^{2^k})\)中
而\([0,P)\)中的数在模\(P\)意义相乘后结果也在\([0,P)\)中
而模\(P\)意义下\(x^{-1}=x^{P-2}\)
那么Nim积意义下\(x^{-1}=x^{2^{2^k}-2}\)
(不是很懂,但是验证下来是对的,会做的大佬可以评论区大力D我
复杂度\(O(n^3log^2A)\)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<string>
#define maxn 155
#define maxm (1<<13)
#define INF 0x3f3f3f3f
#define MOD 998244353
#define ull unsigned long long
using namespace std;
inline long long getint()
{
long long num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n;
ull a[maxn][maxn],f[maxm][maxm],M[10];
bool vis[maxm][maxm];
inline ull mul(ull x,ull y,int p=32)
{
if(x>y)swap(x,y);
if(!x||!y)return 0;
if(x==1)return y;
if(y<maxm&&vis[x][y])return f[x][y];
ull A=x>>p;
ull B=x&((1ull<<p)-1);
ull P=y>>p;
ull Q=y&((1ull<<p)-1);
ull ap=mul(mul(A,P,p>>1),1ull<<p>>1,p>>1),bq=mul(B,Q,p>>1);
ull ret=ap^bq^((mul(A^B,P^Q,p>>1)^bq)<<p);
if(y<maxm)vis[x][y]=1,f[x][y]=ret;
return ret;
}
inline ull ksm(ull num,ull k)
{
ull ret=1;
for(;k;k>>=1,num=mul(num,num))if(k&1)ret=mul(ret,num);
return ret;
}
inline ull getinv(ull x)
{
ull tmp;
for(int i=0;i<=5;i++)
if(x>=M[i])tmp=M[i];
else{tmp=M[i];break;}
return ksm(x,tmp-2);
}
int main()
{
ull k=1;M[0]=2;
for(int i=1;i<=8;i++)
{
k=k+k,M[i]=1;
for(int j=1;j<=k;j++)M[i]=M[i]+M[i];
}
n=getint();
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>a[i][j];
for(int i=1;i<=n;i++)
{
int id=0;
for(int j=i;j<=n;j++)if(a[j][i]){id=j;break;}
if(!id)continue;
for(int j=i;j<=n;j++)swap(a[i][j],a[id][j]);
ull Inv=getinv(a[i][i]);
for(int j=i+1;j<=n;j++)if(a[j][i]!=0)
{
ull tmp=mul(a[j][i],Inv);
for(int k=i;k<=n;k++)a[j][k]^=mul(a[i][k],tmp);
}
}
ull ans=1;
for(int i=1;i<=n;i++)ans=mul(ans,a[i][i]);
if(ans)printf("xiaoDyingle\n");
else printf("xiaoDwandanle\n");
}