洛谷 P4301 [CQOI2013]新Nim游戏 线性基

题目描述

传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。

本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。

如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。

输入输出格式

输入格式:
第一行为整数k。即火柴堆数。

第二行包含k个不超过10^9的正整数,即各堆的火柴个数。

输出格式:
输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。

输入输出样例

输入样例#1:
6
5 5 6 6 5 5
输出样例#1:
21
说明
k<=100

分析:
第一个回合取完后,剩下的堆不能存在一个子集,满足这些堆的数目异或起来等于 0
显然的最大线性无关组。
我们把每个数从大到小排序,依次插入到线性基中,如果这个数无法插入,也就是里面的基底可以表示这个数,那么这个数一定要被取,加入到答案即可。这样贪心是正确的,证明好想需要某些我不懂的东西,记个结论就好了。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#define LL long long

const int maxn=107;
const int maxp=31;

using namespace std;

int n;
int a[maxp+2],b[maxn],bit[maxp+2];
LL ans;

bool cmp(int x,int y)
{
    return x>y;
}

int push(int x)
{   
    int c=0,y=x;
    for (int i=1;i<=maxp;i++)
    {
        if (y&bit[maxp-i])
        {
            if ((!a[i]) && (!c)) c=i;
            y^=a[i];
        }
    }
    if (!y) return x;
    a[c]=y; 
    for (int i=1;i<=maxp;i++)
    {
        if (i==c) continue;
        if (a[i]&bit[maxp-c]) a[i]^=y;
    }
    return 0;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&b[i]);
    sort(b+1,b+n+1,cmp);
    bit[0]=1;
    for (int i=1;i<=maxp;i++) bit[i]=bit[i-1]*2;
    for (int i=1;i<=n;i++) ans+=(LL)push(b[i]);
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81671251