题目链接:https://nanti.jisuanke.com/t/39272
题意:给一棵树,n个结点,树根为1,n-1条边,每个结点有一个权值。进行3种操作:
1 s t:把1和s之间的最短路径上的所有结点|t。
2 s t:把1和s之间的最短路径上的所有结点&t。
3 s t:把1和s之间的最短路径上的所有结点进行异或,若结果等于t则输出NO,否则输出YES。(不理解的话可以看一下nim博弈论)
思路:很明显的一道题树链剖分题,难的是怎么维护线段树。我们拆成二进制来看,用num[32]维护区间内所有点对应位的1的数量,对于或操作来说,若对应位为1,则区间所有点对应位全改为1,若为0,则不操作; 对于与操作来说,若对应位为0,则区间所有点对应位全为0,若为1,则不操作。而且这两种操作是具有覆盖效果的,那么我们可以用懒惰标记add[i]=1表示该区间第i位全为1,add[i]=-1表示该区间第i位全为0。查询操作时,将查询区间上所有位1的数量模2,即区间异或和的对应位,将其与输入的t比较是否相等即可。
AC代码:
#include<cstdio> #include<algorithm> using namespace std; const int maxn=100005; int cnt1,head[maxn]; int dep[maxn],siz[maxn],fa[maxn],son[maxn],top[maxn],cnt2,id[maxn],w[maxn],wt[maxn],res; int n,m; struct node1{ int v,nex; }edge[maxn<<1]; void adde(int u,int v){ edge[++cnt1].v=v; edge[cnt1].nex=head[u]; head[u]=cnt1; } struct node2{ int l,r,len; int num[32],add[32]; }tr[maxn<<2]; void pushup(int v){ for(int i=0;i<30;++i) tr[v].num[i]=tr[v<<1].num[i]+tr[v<<1|1].num[i]; } void pushdown(int v){ for(int i=0;i<30;++i){ if(tr[v].add[i]==1){ tr[v<<1].num[i]=tr[v<<1].len; tr[v<<1].add[i]=1; tr[v<<1|1].num[i]=tr[v<<1|1].len; tr[v<<1|1].add[i]=1; tr[v].add[i]=0; } if(tr[v].add[i]==-1){ tr[v<<1].num[i]=0; tr[v<<1].add[i]=-1; tr[v<<1|1].num[i]=0; tr[v<<1|1].add[i]=-1; tr[v].add[i]=0; } } } void build(int v,int l,int r){ tr[v].l=l,tr[v].r=r,tr[v].len=r-l+1; if(l==r){ for(int i=0;i<30;++i) if((1<<i)&wt[r]) tr[v].num[i]=1; return; } int mid=(l+r)>>1; build(v<<1,l,mid); build(v<<1|1,mid+1,r); pushup(v); } void update(int v,int l,int r,int op,int k){ if(l<=tr[v].l&&r>=tr[v].r){ if(op==1){ for(int i=0;i<30;++i) if((1<<i)&k){ tr[v].num[i]=tr[v].len; tr[v].add[i]=1; } } else{ for(int i=0;i<30;++i) if(!((1<<i)&k)){ tr[v].num[i]=0; tr[v].add[i]=-1; } } return; } pushdown(v); int mid=(tr[v].l+tr[v].r)>>1; if(l<=mid) update(v<<1,l,r,op,k); if(r>mid) update(v<<1|1,l,r,op,k); pushup(v); } void query(int v,int l,int r){ if(l<=tr[v].l&&r>=tr[v].r){ for(int i=0;i<30;++i){ if(tr[v].num[i]&1) res^=(1<<i); } return; } pushdown(v); int mid=(tr[v].l+tr[v].r)>>1; if(l<=mid) query(v<<1,l,r); if(r>mid) query(v<<1|1,l,r); } void dfs1(int x,int f,int deep){ fa[x]=f; dep[x]=deep; siz[x]=1; int maxson=-1; for(int i=head[x];i;i=edge[i].nex){ int y=edge[i].v; if(y==f) continue; dfs1(y,x,deep+1); siz[x]+=siz[y]; if(siz[y]>maxson) son[x]=y,maxson=siz[y]; } } void dfs2(int x,int tp){ id[x]=++cnt2; wt[cnt2]=w[x]; top[x]=tp; if(!son[x]) return; dfs2(son[x],tp); for(int i=head[x];i;i=edge[i].nex){ int y=edge[i].v; if(y==fa[x]||y==son[x]) continue; dfs2(y,y); } } void updRange(int x,int y,int op,int k){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); update(1,id[top[x]],id[x],op,k); x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); update(1,id[x],id[y],op,k); } void qRange(int x,int y){ res=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); query(1,id[top[x]],id[x]); x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); query(1,id[x],id[y]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&w[i]); for(int i=1;i<n;++i){ int x,y; scanf("%d%d",&x,&y); adde(x,y); adde(y,x); } dfs1(1,0,1); dfs2(1,1); build(1,1,n); while(m--){ int op,s,t; scanf("%d%d%d",&op,&s,&t); if(op==1||op==2) updRange(1,s,op,t); else{ qRange(1,s); if(res==t) printf("NO "); else printf("YES "); } } return 0; }