Sample Input1:
2
1 2
3 4
Sample Input2:
2
1 2
2 3
Sample Output1:
xiaoDyingle
Sample Output2:
xiaoDwandanle
题意:现在有 N ! N! N!个N维向量,然后每次找到一个向量x使得向量x中每个元素都大于0。再自己搞出一个新的向量y,使得向量y中每个元素都小于向量x中每个元素。接着选取其中任意多个元素替换掉x,那么就产生了 2 n 2^n 2n个新向量。再删掉向量x。
最后谁没得操作谁就GG了。答案你懂得。
比赛当然木有任何的思路,甚至样例都没有推出来。
直接输出先手胜利有25分,特判一个奇妙的东东有70分,加上暴力有90分。
无奈.jpg
这题是真的奇妙♂。网上只有一个论文,关于二维的,看了好久才看懂。
然后这道题还™的是多维的。
苦想了我两晚上。
首先我们来康康二维的:
二维的问题其实相当于这样:
给你一个平面,然后其中有很多硬币,有的是正的,有的是反的。
现在你要选择一个矩形使得其中右下角的硬币是正的,然后把四个顶点都翻转。
求先手是否必胜。
显然我们利用奇妙的方法做,只会暴力。
那么我们就引出这个NIM积的概念:
x ⊗ y = m e x 0 < = a < x , 0 < = b < y ( ( a ⊗ b ) ⊕ ( a ⊗ y ) ⊕ ( x ⊗ b ) ) x\otimes y=mex_{0<=a
其中 ⊕ \oplus ⊕表示异或。(NIM和)
打表可得:
发现什么性质?
x ⊗ 0 = 0 ⊗ x = 0 x\otimes 0=0\otimes x=0 x⊗0=0⊗x=0
x ⊗ 1 = 1 ⊗ x = x x\otimes 1=1\otimes x=x x⊗1=1⊗x=x
x ⊗ y = y ⊗ x x\otimes y=y\otimes x x⊗y=y⊗x
其实用处不大。
有用的是数学家们弄出来的结论:
如果把 ⊕ \oplus ⊕看做+,把 ⊗ \otimes ⊗看做*
那么这玩意儿满足任何的加和乘的结合律、分配率、交换律等等。
证明我不肥。
然后还有更为惊骇世俗的结论,有关费马数。
x , y < 2 2 k , x ⊗ y < 2 2 k x,y<2^{2^k},x\otimes y<2^{2^k} x,y<22k,x⊗y<22k
x ∗ 2 2 k = x ⊗ 2 2 k x*2^{2^k}=x\otimes 2^{2^k} x∗22k=x⊗22k
2 2 k ⊗ 2 2 k = 3 2 2 2 k 2^{2^k}\otimes2^{2^k}=\frac 3 22^{2^k} 22k⊗22k=2322k
证明我当然不肥。
只要会用就好了:
我们考虑求x和y的NIM积:(设 x < y x
找出最大的k满足: 2 2 k < = x 2^{2^k}<=x 22k<=x设 M = 2 2 k M=2^{2^k} M=22k
那么x和y可以表达成:
x = a ∗ M + b , y = p ∗ M + q x=a*M+b,y=p*M+q x=a∗M+b,y=p∗M+q
那么 x ⊗ y x\otimes y x⊗y(为了方便,下面都用*或+表示)
= ( a ∗ M + b ) ∗ ( p ∗ M + q ) =(a*M+b)*(p*M+q) =(a∗M+b)∗(p∗M+q)
= a ∗ p ∗ M ∗ M + b ∗ p ∗ M + a ∗ q ∗ M + b ∗ q =a*p*M*M+b*p*M+a*q*M+b*q =a∗p∗M∗M+b∗p∗M+a∗q∗M+b∗q
= 3 2 a ∗ p ∗ M + b ∗ p ∗ M + a ∗ q ∗ M + b ∗ q =\frac 3 2a*p*M+b*p*M+a*q*M+b*q =23a∗p∗M+b∗p∗M+a∗q∗M+b∗q
= M ∗ ( a ∗ p + b ∗ p + a ∗ q ) + b ∗ q + a ∗ p ∗ 1 2 ∗ M =M*(a*p+b*p+a*q)+b*q+a*p*\frac 1 2*M =M∗(a∗p+b∗p+a∗q)+b∗q+a∗p∗21∗M
到这一步,其实就差不多了。
那么我们每次递归下去求解上面那些奇怪的东西就好了。
当然,可以暴力求出某些范围内的sg值即可。
这里推荐 ( 0 − 511 , 0 − 511 ) (0-511,0-511) (0−511,0−511)。
时间大概是 O ( l o g 2 x ) O(log^2x) O(log2x)
证明应该是根据它每次做类似于去更号得到的。
回到原来的题目,其实你还是会发现不太会做。
一种暴力的方法:
枚举N!次,把每个独立的游戏都枚举出来。
计算一个向量的sg值其实就相当于取其中两两元素的积,一直这样求下去即可。
然后把这些独立的游戏异或起来即可。
然鹅慢!
考虑优化?
我们发现其中有奇妙的东东:
这玩意儿计算方法其实就是行列式的定义:(虽说百度上面那个定义有点鬼畜,没看懂)
一个n阶行列式中,n个不同行,不同列(编号组成一个排列)的元素的乘积,称为一个项。
行列式的定义:行列式的所有的项乘上(-1)^(排列逆序对个数)的代数和。
那么我们不就可以把上面的定义中乘积定义为NIM积,把和定义为NIM和。
其中那个-1可以不用管它,因为异或的加法逆元是他自己。
大功告成~具体怎么做看下面:
高斯消元求行列式
那么如果算出来的sg值为0,则必败,否则必胜。
然后还有一个小细节:
当求NIM积的逆元时,可以发现其中的费马数是可以用费马小定理来求的。
证明的话,据说其是封闭性的。我太菜了,只能感性理解。
调了好久,最后还T飞了。(原谅我开O3)
#include
#include
#include
#include
#include
#define ull unsigned long long
using namespace std;
int n,f[512][512];
ull M[9],a[201][201];
__attribute__((optimize("-O3")))
inline ull read()
{
ull X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
__attribute__((optimize("-O3")))
ull find(ull x,ull y)
{
if (x==0 || y==0) return 0;
if (x<=511 && y<=511 && f[x][y]!=0) return f[x][y];
if (x<y) swap(x,y);
ull m;
for (register int i=0;i<=5;i++)
{
if (x>=M[i]) m=M[i];
else break;
}
ull a=x/m;
ull b=x & (m-1);
ull p=y/m;
ull q=y & (m-1);
return m*(find(a,p)^find(b,p)^find(a,q))^find(b,q)^find(find(a,p),m/2);
}
__attribute__((optimize("-O3")))
ull qsm(ull a,ull b)
{
ull t=1;
ull y=a;
while (b>0)
{
if ((b&1)==1) t=find(t,y);
y=find(y,y);
b/=2;
}
return t;
}
ull find_ni(ull x)
{
ull m;
for (int i=0;i<=5;i++)
{
if (x>=M[i]) m=M[i];
else
{
m=M[i];
break;
}
}
return qsm(x,m-2);
}
int main()
{
freopen("data.in","r",stdin);
// freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
M[0]=2;
ull k=1;
for (int i=1;i<=8;i++)
{
k=k*2;M[i]=1;
for (int j=1;j<=k;j++)
{
M[i]=M[i]*(ull)2;
}
}
for (int i=1;i<=511;i++)
{
f[1][i]=f[i][1]=i;
}
for (int i=2;i<=511;i++)
{
for (int j=2;j<=511;j++)
{
f[i][j]=find(i,j);
}
}
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
// scanf("%llu",&a[i][j]);
a[i][j]=read();
}
}
for (register int i=1;i<=n;i++)
{
bool pd=true;
int id;
for (register int j=i;j<=n;j++)
{
if (a[j][i]!=0)
{
id=j;
pd=false;
}
}
if (pd==false)
{
for (register int j=i;j<=n;j++)
{
swap(a[id][j],a[i][j]);
}
ull ny=find_ni(a[i][i]);
for (register int j=i;j<=n;j++)
{
a[i][j]=find(a[i][j],ny);
}
for (register int j=i+1;j<=n;j++)
{
if (a[j][i]!=0)
{
ull op=a[j][i];
for (register int k=i;k<=n;k++)
{
a[j][k]=a[j][k]^find(a[i][k],op);
}
}
}
}
}
ull ans=1;
for (int i=1;i<=n;i++)
{
ans=find(ans,a[i][i]);
}
if (ans==0) printf("xiaoDwandanle\n");
else printf("xiaoDyingle\n");
}