• #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;
    }
    
  • 相关阅读:
    常见排序算法的实现
    Never-build package 'XXXX' requires always-build package 'EhLib70'
    自制的七个C,总结的太好了
    那些有影响力的语言
    数海笔记(0)-程序员为什么要学数学
    linux 多线程编程笔记
    双指数边缘平滑滤波器用于磨皮算法的尝试。
    对于树的序列化,用了stream,很好
    很聪明的解法
    按窗口获得最大数 和 中位数
  • 原文地址:https://www.cnblogs.com/ftae/p/6947707.html
Copyright © 2020-2023  润新知