标签:
题意:
给了一串数,个数不超过1e5,这串数是通过题目给的一段代码来生成的
int g = S; for (int i=0; i<N; i++) { a[i] = g; if( a[i] == 0 ) { a[i] = g = W; } if( g%2 == 0 ) { g = (g/2); } else { g = (g/2) ^ W; } }
其中S、N、W都是输入的。
问:从中取连续的一段出来玩Nim博弈,先手赢的取法有多少种。
Nim博弈的结论:每堆异或,最后结果为0的先手输,否则,先手赢;
于是这道题就变成了取连续的一段,异或值不为0的取法数。
N最大有1e5,显然需要O(n)复杂度的方法
异或没什么感觉...如果这道题问的是 取连续的一段,和为X的取法有多少种
那么就很自然的能想到前缀和 第i到第j的总和为sum[j]-sum[i-1]
那联想到这道题,能不能求个前缀异或呢?
对!
而且异或能分为 0 与 非0 两种情况
那么在查询 第i到第j 这一段的异或值时,只需要比较 xor_sum[i] 与 xor_sum[j]
假设xor_sum[i]=X; xor_sum[j]=X;
那么很显然i+1到j的这一段为0 ( X xor 0 = X)
我们只需要记录有多少段为0,用总的n*(n+1)/2去减去就是答案了;
为什么要记录有多少段为0?记录有多少段为0?
我们已经有前缀异或,那么当第二次出现某一值时,这个值 前一次出现的位置 到 本次出现的位置 之间这一段 就是异或值为0的。
因此我们统计异或值为0的要方便的多。
做法就是记录所有的xor_sum出现的次数加起来就好了嘛
1 int a[100005]; 2 map<LL, LL> mp; 3 int main() 4 { 5 int t; 6 scanf("%d", &t); 7 while(t--) 8 { 9 int n, s, w; 10 scanf("%d%d%d", &n, &s, &w); 11 int g = s; 12 for (int i=0; i<n; i++) 13 { 14 a[i] = g; 15 if( a[i] == 0 ) 16 a[i] = g = w; 17 if( g%2 == 0 ) 18 g = (g/2); 19 else 20 g = (g/2) ^ w; 21 } 22 LL xor_sum=0, ans=0; 23 mp.clear(); 24 mp[0]=1; 25 for(int i=0;i<n;i++) 26 { 27 xor_sum^=a[i]; 28 ans+=mp[xor_sum]; 29 mp[xor_sum]++; 30 } 31 print((LL)n*(n+1)/2-ans); 32 } 33 return 0; 34 }
标签:
原文地址:http://www.cnblogs.com/Empress/p/4382075.html