您的位置:首页 > 其它

【 bzoj 3759 】 Hungergame

2015-12-11 17:01 169 查看
  似乎是在美术课上听老师扯淡的时候突然YY出来的?

  如果所有的箱子都是打开的,那么就是Nim游戏,直接看Nim和也就是异或和是否为0,是的话先手就输。但是现在有些箱子是打开的,同样也可以尝试用一般Nim游戏的思路来看这道题。

  根据终态,很容易可以出猜测出如果打开的箱子的Nim和为0的时候先手必败。但是这是有条件的,必须要把最大的的Nim和为0的子集先打开,否则即使当前Nim和为0,后手仍然可以把剩余的Nim和为0的子集打开,这样就会改变状态。

  下面分情况讨论一下。

  如果存在Nim和为0的子集,那么先手先把最大的子集打开,此时未开的箱子里不存在Nim和为0的子集。后手如果在开了的箱里取数,就照Nim游戏那样玩。如果后者打开关着的箱,此时开着的箱的Nim和不为0,同样照Nim游戏那样玩。总之不论怎么搞,先手都可以像普通的Nim游戏一样把Nim和取到0。故此时先手必胜。

  如果不存在Nim和为0的子集,那么先手不论开哪些箱,后手总可以使Nim和改为0,故此时先手必败。

  因此就可以得到先手必胜当且仅当存在子集异或和为0的结论。

  数据范围很小,直接暴力状压就能A。

  时间复杂度O(Tn2n)O(Tn2^n)。那些0ms的是smg = =

[code]#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define For(i,a,b) for(int i=a;i< b;i++)
#define shl(i) (1 << (i))

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

int n , a[21];

void input() {
    n = rd();
    For (i , 0 , n) a[i] = rd();
}

void solve() {
    int all = shl(n);
    For (s , 1 , all) {
        int sg = 0;
        For (i , 0 , n) if (shl(i) & s) sg ^= a[i];
        if (!sg) {
            puts("Yes");
            return;
        }
    }
    puts("No");
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
    #endif
    per (T , rd() , 1) {
        input();
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: