bzoj1022
结论:
先手胜有两种情况
1. Nim和/SG[i]异或和==0,且每堆石子数量都为1
2. Nim和不等于0,且至少有一堆石子数量大于1
证明
情况1 每堆石子数量都为1时,堆数为偶数时先手胜,此时Nim和为0;反之,堆数为奇数时先手必败
情况2 至少有一堆石子数量大于1时
若只有一堆石子数量大于1,此时Nim和!=0,先手可一定可以将局面变为奇数个1,使后手进入必败状态
若有>=2堆石子数量大于1
1. 初始的Nim和0时
* 取石子相当于减小某个数,即把某个数的某个二进制位k由1变为0,再改变低于k的位数
* 二进制位k为1的数有偶数
假设先手将某数a的最高第k位改为0,又改了k之后的某些位,那么后手完全可以也找到另一个二进制第k位为1的数b
在第k位及之后做相同修改。若修改b之后,还存在大于1的数,就这样修改,并循环往复; 否则,后手完全可以不修改b,而是转化为情况 2- 1) 的先手而取胜。此种情况后手必胜
2. 初始的Nim和!=0时:先手可通过调整最大的数,将局面变为 Nim和0,他就成为了上面情况的"后手"。此种情况先手必胜
代码
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define MOD 1000000007
#define inf 2147483640
#define LL long long
#define free(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);
using namespace std;
const int maxn=100010;
struct edge {int to,next;}e[maxn<<1];
priority_queue<int> q;
int head[maxn],ans[maxn],d[maxn],n,m,top,cnt;
void link(int u,int v) {
e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
}
int main() {
int T;scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&m);cnt=0;
memset(head,0,sizeof(head));memset(d,0,sizeof(d));
for (int u,v,i=1;i<=m;i++) {scanf("%d%d",&u,&v);link(v,u);d[u]++;}
top=0;
while(q.size())q.pop();
for(int i=1;i<=n;i++)if(!d[i])q.push(i);
while(q.size()){
int x=q.top();q.pop();
ans[++top]=x;
for(int i=head[x];i;i=e[i].next){
d[e[i].to]--;
if(d[e[i].to]==0)q.push(e[i].to);
}
}
if(top!=n)printf("Impossible!\n");
else {
for (int i=n;i>=1;i--)printf("%d ",ans[i]);
printf("\n");
}
}
return 0;
}

浙公网安备 33010602011771号