【树链剖分】bzoj2819 nim

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2819
题目大意:有一棵树,每个节点上有一堆石子,有两种操作:1.改变某个节点的nim值 2.问在u到v的路径上玩nim游戏是否有先手必胜策略

直接链剖即可,懂点博弈论的都知道:nim游戏中异或和为0的无先手必胜策略,否则均有先手必胜策略

这里注意几点:
1.由于点的数目巨大所以请直接上lowbit或者zkw线段树……

2.由于有可能出现链的情况所以题目里好心提示不要用dfs,然而我偏要用……
结果就是………………
这里写图片描述
这里写图片描述

3.特别注意!!!!!!!!!
这里写图片描述

代码:

#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;
struct Link
{
    int s,t,next;
}l[2000000];
struct Point
{
    int siz,dep,son,fa;
    int top,num;
}p[1000000];
int d[1000000];
int w[1000000];
int g[1000000];
int c[1000000];
stack<int> S;
int n,m,tot = 0,s,t;

int lowbit(int x)
{
    return (x&(-x));
}

void update(int x,int num)
{
    while(x <= n)
    {
        d[x] ^= num;
        x += lowbit(x);
    }
}

int sum(int x)
{
    int ans = 0;
    while(x)
    {
        ans ^= d[x];
        x -= lowbit(x);
    }
    return ans;
}

int sum2(int s,int t)
{
    return (sum(t)^sum(s-1));
}

void dfs1(int x)
{
    p[x].siz = 1;
    p[x].son = 0;
    w[x] = g[x];
    while(w[x])
    {
        if(l[w[x]].t != p[x].fa)
        {
            p[l[w[x]].t].fa = x;
            p[l[w[x]].t].dep = p[x].dep + 1;
            dfs1(l[w[x]].t);
            if(p[l[w[x]].t].siz > p[p[x].son].siz)
                p[x].son = l[w[x]].t;
            p[x].siz += p[l[w[x]].t].siz;
        }
        w[x] = l[w[x]].next;
    }
}

void dfs2(int x)
{
    p[x].top = S.top();
    p[x].num = ++tot;
    if(!p[x].son)
        return ;
    dfs2(p[x].son);
    w[x] = g[x];
    while(w[x])
    {
        if(l[w[x]].t != p[x].fa && l[w[x]].t != p[x].son)
        {
            S.push(l[w[x]].t);
            dfs2(l[w[x]].t);
            S.pop();
        }
        w[x] = l[w[x]].next;
    }
}

void cut_tree()
{
    tot = 0;
    p[1].dep = 1;
    dfs1(1);
    tot = 0;
    p[1].fa = 1;
    S.push(1);
    dfs2(1);
    S.pop();
    for(int i = 1;i <= n;i ++)
        update(p[i].num,c[i]);
}

int treesum(int x,int y)
{
    if(p[x].top == p[y].top)
    {
        if(p[x].num > p[y].num)
            swap(x,y);
        return sum2(p[x].num,p[y].num);
    }
    if(p[p[x].top].dep < p[p[y].top].dep)
        swap(x,y);
    int ans1 = sum2(p[p[x].top].num,p[x].num);
    int ans2 = treesum(p[p[x].top].fa,y);
    return (ans1^ans2);
}

void Add_Link(int s,int t)
{
    l[++tot].s = s;
    l[tot].t = t;
    l[tot].next = g[s];
    g[s] = tot;
}

int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++)
        scanf("%d",&c[i]);
    for(int i = 1;i < n;i ++)
    {
        scanf("%d%d",&s,&t);
        Add_Link(s,t);
        Add_Link(t,s);
    }
    cut_tree();
    scanf("%d",&m);
    for(int i = 1;i <= m;i ++)
    {
        char ch[3];
        scanf("%s%d%d",ch,&s,&t);
        if(ch[0] == 'Q')
        {
            if(treesum(s,t) == 0)
                printf("No\n");
            else
                printf("Yes\n");
        }
        else
        {
            int k = p[s].num;
            update(k,sum2(k,k));
            update(k,t);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值