hdu5274 Dylans loves tree LCA+线段树

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5274
在树上的询问和操作,每次修改单点值,询问两点之间出现次数为奇数的点权是什么,若没有输出-1.询问保证两点间至多只有一个数出现奇数次。

有一种经典的将树上的点转化成序列的方法,我们用dfs遍历这棵树,那么对于一个节点,他一点比他的子树即子节点先访问到,且当他的最后一个子节点的所有子树也都访问完时,这中间访问的节点一定都是他的子树。那么我们可以在访问时做一下记录,每个点首先被访问的clock,和结束时的clock,那么这中间的便是他的子树。

剩下的就可以用线段树维护区间异或和了。
这题有坑的地方就是点权可能为0,处理方法是先把所以的点权都加上1,最后再减1.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#define Lson Ls,L,mid
#define Rson Rs,mid+1,R
using namespace std;
typedef long long ll;
const int N = 2*1e5+10;
const int maxh = 20;

vector<int> g[N];

int anc[N][maxh];
int n,q,u,v,clock;
int st[N],ed[N],deep[N];
int val[N];

void dfs(int x,int pa,int de){
    deep[x] = de;
    st[x] =  ++clock;
    anc[x][0] = pa;
    for(int i=0;i<g[x].size();i++)if(g[x][i]!=pa){
        dfs(g[x][i],x,de+1);
    }
    ed[x] = clock;
}

void init_lca(){
    dfs(1,-1,0);
    for(int k=0;k<maxh-1;k++){
        for(int i=1;i<=n;i++)
            if(anc[i][k]<0) anc[i][k+1] = -1;
            else anc[i][k+1] = anc[anc[i][k]][k];
    }
}

int swim(int x, int H){
    for(int i = 0; H; i++){
        if (H&1) x = anc[x][i];
        H >>= 1;
    }
    return x;
}

int lca(int u,int v){
    if(deep[u]>deep[v]) swap(u,v);
    int H = deep[v]-deep[u];
    v = swim(v,H);
    if(u==v) return u;
    for(int k=maxh-1;k>=0;k--)
        if(anc[u][k]!=anc[v][k]){
            u = anc[u][k];
            v = anc[v][k];
        }
    return anc[u][0];
}



struct Node{
    int val, set;
}tree[N<<2];
inline void update(Node& fa, Node& Ls, Node& Rs){
    fa.val = (Ls.val ^ Rs.val);
}
inline void pushdown(Node& fa, Node& Ls, Node& Rs){
    if (fa.set != 0){
        Ls.val ^= fa.set; Ls.set ^= fa.set;
        Rs.val ^= fa.set; Rs.set ^= fa.set;
        fa.set = 0;
    }
}
void insert(int v, int L, int R, int p, int q, int delta){
    if (p<=L && R<=q){
        tree[v].val ^= delta;
        tree[v].set ^= delta;
        return;
    }
    int Ls = v<<1, Rs = Ls+1, mid = (L+R)/2;
    pushdown(tree[v], tree[Ls], tree[Rs]);
    if (q<=mid) insert(Lson, p, q, delta);
    else if (p>mid) insert(Rson, p, q, delta);
    else{
        insert(Lson, p, q, delta);
        insert(Rson, p, q, delta);
    }
    update(tree[v], tree[Ls], tree[Rs]);
}
int query(int v, int L, int R,int ql, int qr){
    if(ql<=L && R<=qr){
        return tree[v].val;
    }
    int Ls = v<<1, Rs = Ls+1, mid = (L+R)/2;
    pushdown(tree[v], tree[Ls], tree[Rs]);
    int ans = 0;
    if(qr<=mid) ans ^= query(Lson,ql,qr);
    else if(ql>mid) ans ^= query(Rson,ql,qr);
    else{
        ans ^= query(Lson,ql,qr);
        ans ^= query(Rson,ql,qr);
    }
    return ans ;
}



int MAIN(){
    int T;
    cin >> T;
    while(T--){
        clock = 0;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++) g[i].clear();
        for(int i=1;i<=n-1;i++){
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&val[i]);
            val[i]++;
        }

        init_lca();
        memset(tree,0,sizeof(tree));
        for(int i=1;i<=n;i++)
            insert(1,1,n,st[i],ed[i],val[i]);


        for(int i=1;i<=q;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(a==0){
                c++;
                insert(1,1,n,st[b],ed[b],val[b]^c);
                val[b] = c;
            }
            else{
                int wt1 = query(1,1,n,st[b],st[b]);
                int wt2 = query(1,1,n,st[c],st[c]);
                int hc = lca(b,c);
                int wt3 = val[hc];
                int ans = wt1^wt2^wt3;
                printf("%d\n",ans-1);
            }
        }
    }
    return 0;
}
const int main_stack = 16;
char my_stack[128<<20];

int main() {
    __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
    __asm__("movl %%eax, %%esp;\n"::"a"(my_stack + sizeof(my_stack) - main_stack):"%esp");
    MAIN();
    __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值