hdu5274 - Dylans loves tree(树链剖分)

来源:互联网 发布:百度算法大全 编辑:程序博客网 时间:2024/05/16 16:23

Dylans loves tree

Accepts: 37
Submissions: 262
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述

Dylans有一棵N个点的树。每个点有点权。树上节点标号为1∼N。
他得到了Q个询问,形式如下:
①0 x y:把第x个点的点权修改为y。
②1 x y:对于x∼y路径上的每一种点权,是否都出现偶数次?
保证每次询问的路径上最多只有一种点权的出现次数是奇数次。

1≤N,Q≤100000, 点权A[i]∈N,且都 ≤100000

输入描述

第一行一个正整数T表示数据组数(T≤3且最多只有一组数据N>1000)
第一行两个数N、Q表示树的点数和询问个数。
接下来N−1行每行一对数(x,y)表示树上的一条边。
接下来一行N个数表示每个点的点权。
接下来Q行每行三个数(opt,x,y)表示询问。

输出描述

对于每个②询问,如果全是偶数输出“-1”,否则输出出现奇数次的权值。

输入样例

1
3 2
1 2
2 3
1 1 1
1 1 2
1 1 3

输出样例

-1
1

Hint

hack数据里N和Q必须小于等于10000,且对于读入的每一行末尾不应该有多余的空格。
官方题解:
题目里有一个很神奇的性质:路径上最多只有一个数出现奇数次。
这应该马上想到异或。因为异或两次和没异或是等价的。此外异或满足区间减性质。
因为有修改,我们很自然地想到用数据结构维护。
最无脑的就是直接上树链剖分或是Splay维护区间xor值即可。
仔细想一想,发现可以利用LCA消去“树上路径”,转化为根到x路径上求xor值。
我们可以很经典地直接使用线段树或树状数组维护dfs序。
(然而BC不给我卡log2。。。呜呜呜)
有一个很强的trick就是权值可以为0!
所以比如路径上有3个0,虽然他们xor值还是0,但是他们是出现了奇数次。
我特意把A[i]说成∈自然数集而不是[0,100000],就是想尽量不被发现。
怎么避免呢?单独维护0的情况?
有一个很简单的解决方案:直接把读入时所有权值+1,输出的时候再-1即可!
时间复杂度为O(N(log2N)2)或者O(NlogN)

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>#pragma comment(linker,"/STACK:1024000000,1024000000")using namespace std;const int maxn=100010;int N,Q;int head[maxn],tot;int num[maxn];int son[maxn];int w[maxn];int fw[maxn];int fa[maxn];int pos;int top[maxn];int dep[maxn];int A[maxn];struct node{    int v,next;}edge[maxn*2];void init(){    tot=pos=0;    memset(head,-1,sizeof(head));    memset(son,-1,sizeof(son));}void add_edge(int u,int v){    edge[tot].v=v;    edge[tot].next=head[u];    head[u]=tot++;}void dfs1(int u,int f,int depth){    dep[u]=depth;    num[u]=1;    fa[u]=f;    for(int i=head[u];i!=-1;i=edge[i].next)    {        int v=edge[i].v;        if(v==f)continue;        dfs1(v,u,depth+1);        num[u]+=num[v];        if(son[u]==-1||num[son[u]]<num[v])            son[u]=v;    }}void dfs2(int u,int sp){    top[u]=sp;    w[u]=++pos;    fw[pos]=u;    if(son[u]!=-1)dfs2(son[u],sp);    for(int i=head[u];i!=-1;i=edge[i].next)    {        int v=edge[i].v;        if(v!=fa[u]&&v!=son[u])            dfs2(v,v);    }}struct IntervalTree{    int X[maxn<<2];    void build(int o,int l,int r)    {        X[o]=0;        if(l==r)        {            X[o]=A[fw[l]]+1;            return ;        }        int mid=(l+r)>>1;        build(o<<1,l,mid);        build(o<<1|1,mid+1,r);        pushup(o);    }    void pushup(int o)    {        X[o]=((X[o<<1])^(X[o<<1|1]));    }    void update(int o,int l,int r,int x,int y)    {        if(l==r)        {            X[o]=y;            return ;        }        int mid=(l+r)>>1;        if(x<=mid)update(o<<1,l,mid,x,y);        else update(o<<1|1,mid+1,r,x,y);        pushup(o);    }    int query(int o,int l,int r,int q1,int q2)    {        if(q1<=l&&r<=q2)            return X[o];        int mid=(l+r)>>1;        int ans=0;        if(q1<=mid)ans^=query(o<<1,l,mid,q1,q2);        if(q2>mid)ans^=query(o<<1|1,mid+1,r,q1,q2);        return ans;    }}tree;int Query(int x,int y){    int ans=0;    int f1=top[x],f2=top[y];    while(f1!=f2)    {        if(dep[f1]<dep[f2])        {            swap(x,y);            swap(f1,f2);        }        ans^=tree.query(1,1,N,w[f1],w[x]);        x=fa[f1],f1=top[x];    }    if(dep[x]>dep[y])swap(x,y);    ans=(ans^(tree.query(1,1,N,w[x],w[y])));    return ans?ans-1:-1;}int main(){    int T,u,v;    int op,x,y;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&N,&Q);        init();        for(int i=1;i<N;i++)        {            scanf("%d%d",&u,&v);            add_edge(u,v);            add_edge(v,u);        }        for(int i=1;i<=N;i++)scanf("%d",&A[i]);        dfs1(1,0,0);        dfs2(1,1);        tree.build(1,1,N);        while(Q--)        {            scanf("%d%d%d",&op,&x,&y);            if(op==0)tree.update(1,1,N,w[x],y+1);            else printf("%d\n",Query(x,y));        }    }    return 0;}
0 0
原创粉丝点击