zoukankan      html  css  js  c++  java
  • [CQOI2013]新Nim游戏 线性基

    题面

    题面

    题解

    首先我们知道nim游戏先手必败当且仅当所有石堆异或和为0,因此我们的目标就是要使对手拿石堆的时候,无论如何都不能使剩下的石堆异或和为0。
    对于一个局面,如果我们可以选取一些可以凑出0的石堆留下(因为不能全部拿走,所以这里至少要拿一堆),那么显然就先手必败了。
    因此作为先手,我们留给后手的状态必须是一个凑不出0的状态。
    考虑如果一个局面可以凑出0,会具有什么样的特征。
    对于一个局面,我们求出它的线性基,如果在线性基外还有别的01串,那么根据线性基的定义,线性基内的串一定可以凑出外面的那个串,然后我们再把用线性基凑出的串和原来的串xor一下就得到0了。
    因此我们需要使得留下的局面中,所有串都在线性基内。
    所以我们先求出给定串的线性基,然后拿走所有线性基外的串就肯定先手必胜了。
    同时为了使得拿走的尽量少,也就是留下的尽量多,所以我们按串的大小,从小到大往线性基内塞串即可

    #include<bits/stdc++.h>
    using namespace std;
    #define R register int
    #define AC 110
    #define LL long long
    
    int n; LL ans;
    int s[AC], f[AC];
    
    inline int read()
    {
        int x = 0;char c = getchar();
        while(c > '9' || c < '0') c = getchar();
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x;
    }
    
    void pre()
    {
        n = read();
        for(R i = 1; i <= n; i ++) s[i] = read();
        sort(s + 1, s + n + 1);
    }
    
    void work()
    {
        for(R i = n; i; i --)//从高开始贪心
        {
            int x = s[i]; bool done = false;
            for(R j = 30, maxn = 1 << 30; ~j; j --, maxn >>= 1)
            {
                if(!(x & maxn)) continue;
                if(!f[j]) {f[j] = x, done = true; break;}
                else x ^= f[j];
            }
            if(!done) ans += s[i];//没被加入线性基就要拿走
        }
        printf("%lld
    ", ans);
    }
    
    int main()
    {
    //	freopen("in.in", "r", stdin);
        pre();
        work();
    //	fclose(stdin);
        return 0;
    }
    
  • 相关阅读:
    Redis 查看、删除keys
    gitlab 备份和恢复
    gitlab的搭建
    certbot 域名续期及证书查看
    晴天(周杰伦)
    SSH Permission denied (publickey,gssapi-keyex,gssapi-with-mic)
    jenkins miaration section 1
    jenkins 忘记管理员密码
    Yangk's-树状数组 模板
    codeforces-977F-Consecutive Subsequence【动态规划】
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10354328.html
Copyright © 2011-2022 走看看