hdu5274 Dylans loves tree

官方题解
题目里有一个很神奇的性质:路径上最多只有一个数出现奇数次。
这应该马上想到异或。因为异或两次和没异或是等价的。此外异或满足区间减性质。
因为有修改,我们很自然地想到用数据结构维护。
最无脑的就是直接上树链剖分或是Splay维护区间xor值即可。
仔细想一想,发现可以利用LCA消去“树上路径”,转化为根到x路径上求xor值。
我们可以很经典地直接使用线段树或树状数组维护dfs序。
(然而BC不给我卡
  
  
   
   log2
  
  。。。呜呜呜)
有一个很强的trick就是权值可以为0!
所以比如路径上有3个0,虽然他们xor值还是0,但是他们是出现了奇数次。
我特意把
  
  
   
   A[i]
  
  说成∈自然数集而不是
  
  
   
   [0,100000]
  
  ,就是想尽量不被发现。
怎么避免呢?单独维护0的情况?
有一个很简单的解决方案:直接把读入时所有权值+1,输出的时候再-1即可!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn=100005;
int d[maxn];
int deep[maxn];
int dp[maxn][21];
int tree[maxn];
int st[maxn],ed[maxn];
vector<int>g[maxn];
int n,q;
int o;
void dfs(int rt,int f,int sh)
{
    st[rt]=++o;
    deep[rt]=sh;
    if(f==-1)
        dp[rt][0]=rt;
    else
        dp[rt][0]=f;
    for(int i=1;i<=20;i++){
        dp[rt][i]=dp[dp[rt][i-1]][i-1];
    }
    for(int i=0;i<g[rt].size();i++){
        if(g[rt][i]==f)
            continue;
        dfs(g[rt][i],rt,sh+1);
    }
    ed[rt]=o+1;
}
int lca(int a,int b)
{
    int deep1=deep[a];
    int deep2=deep[b];
    if(deep1<deep2){
        swap(deep1,deep2);
        swap(a,b);
    }
    int y=deep1-deep2;
    for(int i=0;i<=20;i++){
        if(y&(1<<i)){
            a=dp[a][i];
        }
    }
    if(a==b)
        return a;
    for(int i=20;i>=0;i--){
        if(dp[a][i]==dp[b][i])
            continue;
        a=dp[a][i];
        b=dp[b][i];
    }
    return dp[a][0];
}
int lowbit(int x)
{
    return x&(-x);
}
void update(int a,int x)
{
    while(a<=o){
        tree[a]^=x;
        a+=lowbit(a);
    }
}
int query(int x)
{
    int ans=0;
    while(x>0){
        ans^=tree[x];
        x-=lowbit(x);
    }
    return ans;
}
int main()
{
    int t;
    int a,b;
    scanf("%d",&t);
    while(t--){
        o=0;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            g[i].clear();
        for(int i=0;i<n-1;i++){
            scanf("%d%d",&a,&b);
            g[a].push_back(b);
            g[b].push_back(a);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&d[i]);
            d[i]++;
        }
        dfs(1,-1,0);
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++){
            update(st[i],d[i]);
            update(ed[i],d[i]);
        }
        int c;
        for(int i=0;i<q;i++){
            scanf("%d%d%d",&c,&a,&b);
            if(c==0){
                update(st[a],d[a]);
                update(ed[a],d[a]);
                d[a]=b+1;
                update(st[a],d[a]);
                update(ed[a],d[a]);
            }
            else{
                int ans=query(st[a])^query(st[b])^d[lca(a,b)];
                if(ans==0)
                    printf("-1\n");
                else
                    printf("%d\n",ans-1);
            }
        }
    }
    return 0;
}




  
  
   
   O(Nlog(N))  

  
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值