[HNOI2007]分裂游戏

题目大意:
  有n个格子,每个格子有一些石子,其中第i个格子中有p[i]个石子。
  有两个人A和B轮流进行操作,每次可以选定三个格子i<j<=k,从i中取出一个石子,并在j和k中各放入一个石子。
  不能进行操作的人输。
  问先手是否有必胜策略,如果有,请输出第一次操做的取法。
  如果第一次可以有多种操作,求出字典序最小的,并求出取法的个数。

思路:
  如果用不同瓶子中石子的个数作为游戏的状态,显然存不下。
  这里我们用一个石子所在的格子编号表示游戏状态。
  若当前有一个石子在i格子,那么它的后继状态就是p[i]-1,p[j]+1,p[k]+1。
  则sg(i)=mex{sg(j)^sg(k)|i<j<=k}。
  由于n的范围比较小,最后枚举所有的i,j,k,暴力求出答案即可。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<vector>
 4 #include<cstring>
 5 #include<algorithm>
 6 inline int getint() {
 7     register char ch;
 8     register bool neg=false;
 9     while(!isdigit(ch=getchar())) if(ch=='-') neg=true;
10     register int x=ch^'0';
11     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
12     return neg?-x:x;
13 }
14 const int N=22;
15 int p[N],sg[N];
16 class Set {
17     private:
18         std::vector<int> v;
19     public:
20         Set() {
21             v.clear();
22             v.push_back(-1);
23         }
24         void insert(const int x) {
25             v.push_back(x);
26         }
27         int mex() {
28             std::sort(v.begin(),v.end());
29             v.resize(std::unique(v.begin(),v.end())-v.begin());
30             for(register std::vector<int>::iterator i=v.begin();i!=v.end()-1;i++) {
31                 if(*(i+1)!=*i+1) return *i+1;
32             }
33             return *(v.end()-1)+1;
34         }
35 };
36 int n;
37 int getsg(const int &i) {
38     if(~sg[i]) return sg[i];
39     Set s;
40     for(int j=i+1;j<=n;j++) {
41         for(int k=j;k<=n;k++) {
42             s.insert(getsg(j)^getsg(k));
43         }
44     }
45     return sg[i]=s.mex();
46 }
47 int main() {
48     for(register int T=getint();T;T--) {
49         n=getint();
50         for(register int i=1;i<=n;i++) {
51             p[i]=getint();
52         }
53         memset(sg,-1,sizeof sg);
54         sg[n]=0;
55         int ans=0;
56         for(register int i=1;i<n;i++) {
57             if(!p[i]) continue;
58             for(register int j=i+1;j<=n;j++) {
59                 for(register int k=j;k<=n;k++) {
60                     p[i]--,p[j]++,p[k]++;
61                     int tmp=0;
62                     for(register int l=1;l<=n;l++) {
63                         if(p[l]&1) {
64                             tmp^=getsg(l);
65                         }
66                     }
67                     if(!tmp) {
68                         if(!ans) {
69                             printf("%d %d %d\n",i-1,j-1,k-1);
70                         }
71                         ans++;
72                     }
73                     p[i]++,p[j]--,p[k]--;
74                 }
75             }
76         }
77         if(!ans) puts("-1 -1 -1");
78         printf("%d\n",ans);
79     }
80     return 0;
81 }

 

posted @ 2017-09-27 14:58  skylee03  阅读(121)  评论(0编辑  收藏  举报