• BZOJ2819 Nim 【dfn序 + lca + 博弈论】


    题目

    著名游戏设计师vfleaking,最近迷上了Nim。普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取。谁不能取谁输。这个游戏是有必胜策略的。于是vfleaking决定写一个玩Nim游戏的平台来坑玩家。
    为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,…n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达。然后他不停地进行如下操作:

    1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家。
    2.把堆v中的石子数变为k。

    由于vfleaking太懒了,他懒得自己动手了。请写个程序帮帮他吧。

    输入格式

    第一行一个数n,表示有多少堆石子。
    接下来的一行,第i个数表示第i堆里有多少石子。
    接下来n-1行,每行两个数v,u,代表v,u间有一条边直接相连。
    接下来一个数q,代表操作的个数。
    接下来q行,每行开始有一个字符:
    如果是Q,那么后面有两个数v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略。
    如果是C,那么后面有两个数v,k,代表把堆v中的石子数变为k。

    对于100%的数据:
    1≤N≤500000, 1≤Q≤500000, 0≤任何时候每堆石子的个数≤32767
    其中有30%的数据:
    石子堆组成了一条链,这3个点会导致你DFS时爆栈(也许你不用DFS?)。其它的数据DFS目测不会爆。

    注意:石子数的范围是0到INT_MAX

    输出格式

    对于每个Q,输出一行Yes或No,代表对询问的回答。

    输入样例

    5

    1 3 5 2 5

    1 5

    3 5

    2 5

    1 4

    6

    Q 1 2

    Q 3 5

    C 3 7

    Q 1 2

    Q 2 4

    Q 5 3

    输出样例

    Yes

    No

    Yes

    Yes

    Yes

    题解

    14s,还以为要T了QAQ
    额。其实和博弈论是有那么点关系的。。其实就是一个众所周知博弈论结论:

    Nim游戏先手必胜,当且仅当石子数的异或和不为0

    说白了就是维护路径异或和
    树剖?应该可以吧。。。【题目说会爆栈?】
    我选择了求dfs序【然而还是要dfs。。】
    我们开一棵线段树维护每个dfs序对应点到根的异或和
    询问 询问u和v对应的异或和,再异或上lca的权值
    修改 一个点的权值修改,只会影响到其子树,利用dfn就可以在线段树上修改

    复杂度O(nlogn)
    常数大得飞起

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
    using namespace std;
    const int maxn = 500005,maxm = 1000005,INF = 1000000000;
    inline int RD(){
        int out = 0,flag = 1; char c = getchar();
        while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
        while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
        return out * flag;
    }
    int h[maxn],ne = 0;
    struct EDGE{int to,nxt;}ed[maxm];
    inline void build(int u,int v){
        ed[ne] = (EDGE){v,h[u]}; h[u] = ne++;
        ed[ne] = (EDGE){u,h[v]}; h[v] = ne++;
    }
    int dfn[maxn],fa[maxn][20],siz[maxn],dep[maxn],V[maxn],w[maxn],id[maxn],n,cnt = 0;
    void dfs(int u){
        dfn[u] = ++cnt; siz[u] = 1; id[cnt] = u;
        REP(i,19) fa[u][i] = fa[fa[u][i - 1]][i - 1];
        Redge(u) if (ed[k].to != fa[u][0]){
            fa[ed[k].to][0] = u; dep[ed[k].to] = dep[u] + 1; w[ed[k].to] ^= w[u];
            dfs(ed[k].to);
            siz[u] += siz[ed[k].to];
        }
    }
    int sum[4 * maxn],lazy[4 * maxn],L,R;
    inline void Build(int u,int l,int r){
        if (l == r) {sum[u] = w[id[l]]; return;}
        int mid = l + r >> 1;
        Build(u << 1,l,mid);
        Build(u << 1 | 1,mid + 1,r);
        sum[u] = sum[u << 1] ^ sum[u << 1 | 1];
    }
    inline void pd(int u,int l,int r){
        int mid = l + r >> 1;
        sum[u << 1] ^= ((mid - l + 1) & 1) * lazy[u];
        sum[u << 1 | 1] ^= ((r - mid) & 1) * lazy[u];
        lazy[u << 1] ^= lazy[u]; lazy[u << 1 | 1] ^= lazy[u];
        lazy[u] = 0;
    }
    void modify(int u,int l,int r,int v){
        if (l >= L && r <= R) {sum[u] ^= ((r - l + 1) & 1) * v; lazy[u] ^= v; return;}
        if (lazy[u]) pd(u,l,r);
        int mid = l + r >> 1;
        if (mid >= L) modify(u << 1,l,mid,v);
        if (mid < R) modify(u << 1 | 1,mid + 1,r,v);
        sum[u] = sum[u << 1] ^ sum[u << 1 | 1];
    }
    int query(int u,int l,int r){
        if (l >= L && r <= R) return sum[u];
        if (lazy[u]) pd(u,l,r);
        int mid = l + r >> 1;
        if (mid >= R) return query(u << 1,l,mid);
        else if (mid < L) return query(u << 1 | 1,mid + 1,r);
        else return query(u << 1,l,mid) + query(u << 1 | 1,mid + 1,r);
    }
    int lca(int u,int v){
        if (dep[u] < dep[v]) swap(u,v);
        int d = dep[u] - dep[v];
        for (int i = 0; (1 << i) <= d; i++)
            if ((1 << i) & d) u = fa[u][i];
        if (u == v) return u;
        for (int i = 19; i >= 0; i--)
            if (fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
        return fa[u][0];
    }
    void Query(int u,int v){
        int a,b,c;
        L = R = dfn[u]; a = query(1,1,n);
        L = R = dfn[v]; b = query(1,1,n);
        c = V[lca(u,v)];
        if (a ^ b ^ c) puts("Yes");
        else puts("No");
    }
    void Change(int u,int v){
        L = dfn[u]; R = dfn[u] + siz[u] - 1;
        modify(1,1,n,V[u] ^ v); V[u] = v;
    }
    int main(){
        memset(h,-1,sizeof(h));
        n = RD();
        REP(i,n) w[i] = V[i] = RD();
        REP(i,n - 1) build(RD(),RD());
        dep[1] = 1; dfs(1);
        Build(1,1,n);
        int Q = RD(),u,v; char cmd;
        while (Q--){
            cmd = getchar();
            while (cmd != 'Q' && cmd != 'C') cmd = getchar();
            u = RD(); v = RD();
            if (cmd == 'Q') Query(u,v);
            else Change(u,v);
        }
        return 0;
    }
    
  • 相关阅读:
    SpringBoot简单项目学习笔记08(servlet的内置容器的切换(tomcat、jetty、undertow))
    读《大家看的设计书(第三版)》有感
    百度比赛任务二收获
    百度前端任务一学习的知识
    学习git的内容
    codeforces #588 ABCD
    codeforces #597 div2 ABCD !F
    codeforces #589 div2 ABCD E待补
    codeforces #590 div3 BCDF E待补
    codeforces #591 div2 ABCD
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282744.html
Copyright © 2020-2023  润新知