jia1saurus’s diary

TooDifficult

CF768E Game of Stones(状压DP+博弈论)

题目大意:

给出n堆石子,A和B来取,一次可以取一堆中的若干个,本身是nim游戏的框架,不过多加了这样的一个限制:

如果其中一堆之前已经被取过k个了,那么就不能再取k个

 

题解:

还是把每一堆石子看成一个子游戏,最后异或起来即可

然后就是求SG函数(dp)

利用状压,把每一个数(1到60)有没有取的状态压到一个60位的2进制里

如果每个状态都计算,显然会超时

但是实际上并没有特别多的状态可以取,实际状态数大概只有60这个数的有限制的整数拆分方案个数

这样就完成状态的压缩

dp[i][s]表示已经取了s这个状态代表的数,现在还有i个石子的SG值

转移就是按照SG函数的定义,往能够达到的局面转移,过程可以利用记忆化搜索

为了节省空间,这里利用map来实现整个过程

#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
map<pair<int, long long>, int> dp;
map<pair<int, long long>, bool> visit;
int dfs(int x, long long S){
    S = S&((1LL<<x)-1);
    if(x == 0) return 0;
    if(visit[{x, S}]) return dp[{x, S}];
    vector<bool> f(63, 0);
    for(int i = x; i >= 1; i--){
        long long Si = 1<<(i-1);
        if(S&Si) continue;
        f[dfs(x-i, S|Si)] = 1;
    }
    for(int i = 0; i < 63; i++) if(!f[i]){
        visit[{x, S}] = true;
        return dp[{x, S}] = i;
    }
}

int main()
{
    int n, x;
    cin>>n;
    for(int i = 60; i >= 0; i--) dfs(i, 0);
    int SG = 0;
    for(int i = 0; i < n; i++) scanf("%d", &x), SG ^= dp[{x, 0}];
    if(SG) cout<<"NO"<<endl;
    else cout<<"YES"<<endl;

}