P3950 - 部落冲突(树链剖分)
此题用树链剖分处理
操作1:询问p点的工人是否能够到达q点
操作2:p点部落与q点开战
操作3:第x次战争结束
当p点与q点开战时工人无法通过两点间的路径,因此我们可以令开战时的路径权值为1,未开战时为0,则工人能到达q点的条件是:当且仅当p点到q点的路径权值和为0
需要注意的是,树链剖分是用来处理树上的点值的,我们可以将边权值同一下放到深度较大的那一点上。具体处理权值时,我们需要在增加点权值时减去两点间的LCA(减去点权值时加上LCA),查询时减去两点间的LCA,此时可以把边权值的维护转换为点权值的维护,于是可以用树链剖分解决
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using namespace std; #define lid (id << 1) #define rid (id << 1) | 1 typedef long long LL; const int maxn=400010; struct Edge{ int nex; int to; }E[2*maxn]; int rt,n,m,r,cnt,tot; int a[2*maxn]; int head[maxn]; int f[maxn];//节点u的父亲节点 int d[maxn];//节点u的深度 int size[maxn];//以u为根的子树节点个数 int son[maxn];//重儿子 int rk[maxn];//当前dfs标号在树中所对应的节点 int top[maxn];//当前节点所在链的顶端节点 int id[maxn];//树中每个节点剖分以后的新编号(决定DFS执行顺序) int rem[maxn]; int mod=0x3f3f3f3f; struct seg_tree { LL l, r; LL lazy; long long sum; } tree[maxn << 2]; void build(LL id, LL l, LL r) { tree[id].l = l; tree[id].r = r; if (l == r) { tree[id].sum = a[rk[l]]; return; } int mid = (l + r) >> 1; build(lid, l, mid); build(rid, mid + 1, r); tree[id].sum = (tree[lid].sum + tree[rid].sum)%mod; } void pushdown(LL id) { if (tree[id].lazy != 0 && tree[id].l != tree[id].r) { LL val = tree[id].lazy; (tree[lid].lazy += val)%=mod; (tree[rid].lazy += val)%=mod; tree[lid].sum += val * (tree[lid].r - tree[lid].l + 1); tree[rid].sum += val * (tree[rid].r - tree[rid].l + 1); tree[id].lazy = 0; } } void add(LL id, LL val,LL l, LL r) { pushdown(id); if (tree[id].l == l && tree[id].r == r) { tree[id].lazy += val; tree[id].sum += val * (r - l + 1); tree[id].sum%=mod; return; } LL mid = (tree[id].l + tree[id].r) >> 1; if (mid >= r) add(lid, val, l, r); else if (mid < l) add(rid, val, l, r); else { add(lid, val, l, mid); add(rid, val, mid + 1, r); } tree[id].sum = (tree[lid].sum + tree[rid].sum)%mod; } long long query(LL id,LL l, LL r) { pushdown(id); if (tree[id].l == l && tree[id].r == r) return tree[id].sum; int mid = (tree[id].l + tree[id].r) >> 1; if (mid >= r) return query(lid, l, r)%mod; else if (mid < l) return query(rid, l, r)%mod; else return (query(lid, l, mid) + query(rid, mid + 1, r))%mod; } void addedge(int u,int v) { E[++tot].nex=head[u]; E[tot].to=v; head[u]=tot; } void dfs1(int u,int fa,int depth) { f[u]=fa; d[u]=depth; size[u]=1; for(int i=head[u];i;i=E[i].nex) { int v=E[i].to; if(v==fa)continue; dfs1(v,u,depth+1); size[u]+=size[v]; if(size[v]>size[son[u]]) { son[u]=v; }//选取重儿子 } } void dfs2(int u,int t)//t为重链顶端,连接重链,记录dfs,处理top,id,rk以保证dfs序连续 { top[u]=t; id[u]=++cnt;//记录dfs序 rk[cnt]=u;//记录相应dfs序的节点 if(!son[u]) { return; } dfs2(son[u],t); for(int i=head[u];i;i=E[i].nex) { int v=E[i].to; if(v!=son[u]&&v!=f[u]) dfs2(v,v); //轻链底端结点即为重链的顶端 } } int sum(int x,int y) { int ans=0; int fx=top[x]; int fy=top[y]; while(top[x]!=top[y]) { if(d[top[x]]<d[top[y]])swap(x,y); ans+=query(1,id[top[x]],id[x]); x=f[top[x]]; } if(id[x]>id[y])swap(x,y); ans+=query(1,id[x],id[y]); ans-=query(1,id[x],id[x]); return ans; } void update(int x,int y,int c) { while(top[x]!=top[y]) { if(d[top[x]]<d[top[y]]) { swap(x,y); } add(1,c,id[top[x]],id[x]); x=f[top[x]]; } if(id[x]>id[y])swap(x,y); add(1,c,id[x],id[y]); add(1,-c,id[x],id[x]); } int tot1[maxn]; int tot2[maxn]; int toto; int main(){ scanf("%d%d",&n,&m); r=1; for(int i=1;i<=n;i++) { a[i]=0; } for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } dfs1(r,0,1); dfs2(r,r); rt=1; build(1,1,n); for(int i=1;i<=m;i++) { char s[5]; int x,y,z; cin>>s; if(s[0]=='C') { scanf("%d%d",&x,&y); update(x,y,1); tot1[++toto]=x; tot2[toto]=y; } if(s[0]=='Q'){ scanf("%d%d",&x,&y); if((sum(x,y))!=0) { printf("No\n"); } else printf("Yes\n"); } if(s[0]=='U') { scanf("%d",&x); update(tot1[x],tot2[x],-1); } } return 0; //处理深度d以及父节点f }

浙公网安备 33010602011771号