ZOJ3591_Nim

题目的意思是给你n个ai,有多少种不同的连续段使得用该段数中所有的数字玩Nim游戏的先手必胜。

首先根据博弈论的知识,我们知道,要使先手必胜,那么只要保证所有的数的异或值不为0就可以了。

这个题目,给的ai的求法给了我很大的误导,我一开始以为要从哪里入手来突破这个题目。结果。。。。。。。深坑啊。

是这样来考虑的,我们直接模拟,分别得出所有的ai的值,然后用一个数字fi表示a1到ai连续的数的异或值。

这样要求异或不为0的情况,我们只要求出所有值为0的情况,然后将所有的情况减去这些情况就可以了。

对于求所有值为0的情况,有两种可能:

一、f[i]=0,说明从1到i的数所有的数的异或值就为0,这就是一个满足条件的段哦。

二、存在1<=i<j<=n,f[i]=f[j],这说明i+1到j这一段的所有的异或值为0(异或的性质哦),这也是一个满足条件的段。

有了上面的两点,我们就可以进行统计了。

统计有两种方法:

一、排个序,然后对于值相同的两两组合就好了。

二、用map哈希的方法记录每一个数出现了多少种情况,然后组合一下就好了。

 

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <map>
 4 #include <algorithm>
 5 #include <cstring>
 6 #define ll long long
 7 #define maxn 100100
 8 using namespace std;
 9 
10 int n,m,k,a[maxn],S,W,t;
11 ll ans,tot;
12 
13 int main()
14 {
15     cin>>t;
16     while (t--)
17     {
18         scanf("%d%d%d",&n,&S,&W);
19         ans=(ll)n*(n+1)/2,tot=0;
20         int g = S;
21         for (int i=1; i<=n; i++)
22         {
23             a[i] = g;
24             if( a[i] == 0 ) { a[i] = g = W; }
25             if( g%2 == 0 ) { g = (g/2); }
26             else           { g = (g/2) ^ W; }
27         }
28         k=0;
29         for (int i=1; i<=n; i++)
30         {
31             k^=a[i];
32             a[i]=k;
33         }
34         sort(a+1,a+1+n);
35         k=1,a[n+1]=a[0]=-1;
36         for (int i=2; i<=n+1; i++)
37         {
38             if (a[i]==a[i-1]) k++;
39             else
40             {
41                 if (a[i-1]==0) tot+=k;
42                 tot+=(ll)k*(k-1)/2;
43                 k=1;
44             }
45         }
46         printf("%lld\n",ans-tot);
47     }
48     return 0;
49 }

 

 

 

posted @ 2013-10-18 20:30  092000  阅读(359)  评论(0编辑  收藏  举报