Description
Input
输入共N +1行:
Output
Sample Input
Sample Input1:
2
1 2
3 4
Sample Input2:
2
1 2
2 3
Sample Output
Sample Output1:
xiaoDyingle
Sample Output2:
xiaoDwandanle
Data Constraint
赛时
题意:现在有
N
!
N!
N!个N维向量,然后每次找到一个向量x使得向量x中每个元素都大于0。再自己搞出一个新的向量y,使得向量y中每个元素都小于向量x中每个元素。接着选取其中任意多个元素替换掉x,那么就产生了
2
n
2^n
2n个新向量。再删掉向量x。
最后谁没得操作谁就GG了。答案你懂得。
比赛当然木有任何的思路,甚至样例都没有推出来。
直接输出先手胜利有25分,特判一个奇妙的东东有70分,加上暴力有90分。
无奈.jpg
题解
这题是真的奇妙♂。网上只有一个论文,关于二维的,看了好久才看懂。
然后这道题还™的是多维的。
苦想了我两晚上。
首先我们来康康二维的:
二维的问题其实相当于这样:
给你一个平面,然后其中有很多硬币,有的是正的,有的是反的。
现在你要选择一个矩形使得其中右下角的硬币是正的,然后把四个顶点都翻转。
求先手是否必胜。
显然我们利用奇妙的方法做,只会暴力。
NIM积
那么我们就引出这个NIM积的概念:
x
⊗
y
=
m
e
x
0
<
=
a
<
x
,
0
<
=
b
<
y
(
(
a
⊗
b
)
⊕
(
a
⊗
y
)
⊕
(
x
⊗
b
)
)
x\otimes y=mex_{0<=a<x,0<=b<y}((a\otimes b)\oplus(a\otimes y)\oplus(x\otimes b))
x⊗y=mex0<=a<x,0<=b<y((a⊗b)⊕(a⊗y)⊕(x⊗b))
其中
⊕
\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<y
x<y)
找出最大的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 <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cctype>
#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");
}