#417 Div2 E (树上阶梯博弈)

#417 Div2 E

题意

给出一颗苹果树,设定所有叶子节点的深度全是奇数或偶数,并且包括根在内的所有节点上都有若干个苹果。
两人进行游戏,每回合每个人可以做下列两种操作中的一种:

  • 每个人可以吃掉某个叶子节点上的部分苹果。
  • 将某个非叶子结点上的部分苹果移向它的孩子。

吃掉树上最后一个苹果的人获胜。
后手可以在游戏开始之前交换两个不同节点的苹果,输出交换后能使得后手胜利的交换总的方案数。

分析

其实就是阶梯博弈裸题。
分两种情况:

  1. 叶子节点深度为奇数,那么只需要对所有深度为奇数的节点求异或和(Nim博弈),异或和等于 0 时先手必败,无论必败方怎么操作,必胜方都可以通过适当的操作抵消掉必败方的操作。在求方案数的时候,对于异或和为 0 的情况,分别在奇数深度节点和偶数深度节点内进行交换,然后遍历奇数深度的节点,从偶数深度节点内找到值为 xor_sum^odd[i] 的节点。
  2. 叶子节点深度为偶数,对深度为偶数的节点求异或和,其它同理。

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
const int N = 1e7 + 5;
int n;
int has[MAXN];
int dep[MAXN];
vector<int> G[MAXN];
int oddxor, evenxor;
ll even, odd;
int evenn[N], oddn[N];
vector<int> oddv, evenv;
int f;
void dfs(int pre, int x, int d) {
    dep[x] = d;
    if(d & 1) {
        odd++;
        oddv.push_back(x);
        oddn[has[x]]++;
        oddxor ^= has[x];
    } else {
        even++;
        evenv.push_back(x);
        evenn[has[x]]++;
        evenxor ^= has[x];
    }
    if(!G[x].size() && dep[x] & 1) f = 1;
    for(int i = 0; i < G[x].size(); i++) {
        int v = G[x][i];
        if(v != pre) dfs(x, v, d + 1);
    }
}
int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> has[i];
    }
    for(int i = 2; i <= n; i++) {
        int x;
        cin >> x;
        G[x].push_back(i);
    }
    ll ans = 0;
    dfs(0, 1, 0);
    if(f) {
        if(!oddxor) {
            ans += (even * even - even) / 2 + (odd * odd - odd) / 2;
        }
        for(int i = 0; i < oddv.size(); i++) {
            if((oddxor ^ has[oddv[i]]) < N) ans += evenn[oddxor ^ has[oddv[i]]];
        }
    } else {
        if(!evenxor) {
            ans += (even * even - even) / 2 + (odd * odd - odd) / 2;
        }
        for(int i = 0; i < evenv.size(); i++) {
            if((evenxor ^ has[evenv[i]]) < N) ans += oddn[evenxor ^ has[evenv[i]]];
        }
    }
    cout << ans << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/ftae/p/6947707.html