Zoj 3591 Nim 【博弈】【搜索】

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3591

题目大意:给你T组case,每组case有N,S,W三个数字,根据题目给出的代码可以算出每组石子个数a[i]。

已知了N组石子的个数,现在让你来选出连续的石子堆,使得先手会赢,问有多少种选法。

比如给出的样例:NSW分别是 3 1 1

则算出来的每堆石子的个数a[i]是 1 1 1,则 1 1 1三堆石子的选法有:

选第一堆,选第二堆,选第三堆,选第一堆到第三堆。

我们分别对第i堆取^,得到的值c[i]是:1  0 1

得到c[i]的值之后,我们可以有这样的想法:比如c[1]和c[3]取^之后是0,说明你选择第二堆和第三堆是不能先手赢的。c[1]和c[2]取^之后是1,说明单选第二堆是先手赢的。为了表示出来第一堆,我们把c[0]赋值为0。

其实也就是在数组c[i]中找有没有相同的。上组给出的1 0 1 加上 c[0]=0, 其实是0 1 0 1 ,有两个0,两个1,说明选了两个0或者两个1都是不满足条件的选择,而总的选择是:C(N,2),也就是总的选择减去不能满足条件的选择。

不能满足条件的选择是C(num,2)的和。

#include<iostream>
#include<stdio.h>
#include<map>
using namespace std;
#define LL long long

int main ()
{
    map<LL, LL > v;
    LL T;
    LL a[100005];
    LL c[100005];
    scanf("%lld",&T);
    while(T--)
    {
        LL ans = 0;
        v.clear();
        LL N,S,W;
        scanf("%lld%lld%lld",&N,&S,&W);
        LL g = S;
        for (LL 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;
        }
//        prLLf("石头的数量: ");
//        for(LL i=0;i<N;i++)
//            prLLf("%d ",a[i]);
//        prLLf("\n");

        c[0]=a[0];
        for(LL i=1;i<N;i++)
            c[i]=c[i-1]^a[i];

//        prLLf("取模之后的数量:");
//        for(LL i=0;i<N;i++)
//            prLLf("%d ",c[i]);
//        prLLf("\n");

        v[0]++;
        for(LL i=0;i<N;i++)
            v[c[i]]++;
        for(map<LL,LL>::iterator shit=v.begin();shit!=v.end();shit++){
            if(shit->second > 1) {
                LL num = shit->second;
                ans += num*(num-1)/2;
            }
        }
        LL sum = N+N*(N-1)/2;
        cout<<sum-ans<<endl;
    }
}
时间: 2024-08-30 03:10:15

Zoj 3591 Nim 【博弈】【搜索】的相关文章

ZOJ 3591 Nim (连续子序列异或和)

题目链接:ZOJ 3591 Nim 题意:根据题目给出的代码得到n堆石头的各自的数量,求先手选出连续的若干堆并且必胜的方法数.(比如:3,1,1 每堆石头数是1,1,1.先手选出(1),(1),(1),(1,1,1) 这四种方案是必胜的,所以答案是4) 思路:在n堆取石头首先想到的是Nim博弈,连续的若干堆,即求连续子序列异或和为0的数量m,n*(n+1)/2-m就是答案 (Nim博弈结论,a1,a2,a3--an,an-1,若a1^a2^a3^--^an^an-1=0先手必败 ) 连续子序列异

ZOJ 3591 Nim 前缀和+位运算

Nim Time Limit: 3 Seconds      Memory Limit: 65536 KB Nim is a mathematical game of strategy in which two players take turns removing objects from distinct heaps. The game ends when one of the players is unable to remove object in his/her turn. This

zoj 3591 Nim

Nim Time Limit: 3 Seconds      Memory Limit: 65536 KB Nim is a mathematical game of strategy in which two players take turns removing objects from distinct heaps. The game ends when one of the players is unable to remove object in his/her turn. This

zoj3591 Nim(Nim博弈)

ZOJ 3591 Nim(Nim博弈) 题目意思是说有n堆石子,Alice只能从中选出连续的几堆来玩Nim博弈,现在问Alice想要获胜有多少种方法(即有多少种选择方式). 方法是这样的,由于Nim博弈必胜的条件是所有数的抑或值不为0,证明见  点击  ,所以答案就转化为原序列有多少个区间的亦或值为0,用n*(n+1) / 2 减去这个值就可以了. 而求有多少个区间的亦或值为0,实际上就是求对于亦或值的前缀nim[i],满足nim[i] == nim[j] 的对数,这时只要对nim数组排序就可以

ZOJ 3529 A Game Between Alice and Bob (分解质因数+Nim博弈)

A Game Between Alice and Bob Time Limit: 5 Seconds      Memory Limit: 262144 KB Alice and Bob play the following game. A series of numbers is written on the blackboard. Alice and Bob take turns choosing one of the numbers, and replace it with one of

UVA 1559 - Nim(博弈dp)

UVA 1559 - Nim 题目链接 题意:一开始有s个石子,2n个人轮流取石子,每个人有个最大能取数目,2n个人奇数一队,偶数一队,取到最后一个石子的队输,问谁赢 思路:记忆化搜索,每个人取的时候对应的后继状态如果有一个必败态,则该状态为必胜态,如果都是必胜态,则该状态为必败态 代码: #include <stdio.h> #include <string.h> int n, s, m[25], dp[25][10005]; int dfs(int now, int state

ACM学习历程—HDU 3915 Game(Nim博弈 &amp;&amp; xor高斯消元)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3915 题目大意是给了n个堆,然后去掉一些堆,使得先手变成必败局势. 首先这是个Nim博弈,必败局势是所有xor和为0. 那么自然变成了n个数里面取出一些数,使得xor和为0,求取法数. 首先由xor高斯消元得到一组向量基,但是这些向量基是无法表示0的. 所以要表示0,必须有若干0来表示,所以n-row就是消元结束后0的个数,那么2^(n-row)就是能组成0的种数. 对n==row特判一下. 代码:

UVA 11859 Division Game (Nim博弈)

题目:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=32746 题意:有一个n*m(1<=n,m<=50)矩阵,每个元素均为2~10000之间的正整数,两个游戏者轮流操作.每次可以选一行中的1个或者大于1的整数,把他们中的每个数都变成它的某个真因子,比如12可以边长1,2,3,4或者6,不能操作的输. 分析:考虑每个数包含的素因子个数(比如12=2*2*3包含3个素因子),则让一个数"变成它的素因子"

hdu 5011 (nim博弈模版)

//nim博弈 //有n堆石头,两人轮流每次从一堆中拿至少1,之多全部的石头,没有石头可拿为lose //判断先手是win还是lose # include <stdio.h> # include <algorithm> # include <string.h> using namespace std; int main() { int n,i; __int64 a,sum; while(~scanf("%d",&n)) { sum=0; fo