P7735-[NOI2021]輕重邊【樹鏈剖分,線段樹】
阿新 • • 發佈:2022-02-13
前言
之前線上賽就A的題現在才寫部落格
正題
題目連結:https://www.luogu.com.cn/problem/P7735
題目大意
有\(n\)個點的一棵樹,開始所有邊都是輕邊,\(m\)次操作。
- 把\(x\rightarrow y\)路徑上所有點連線的重邊都變為輕邊,然後再把路徑上的邊變成重邊。
- 詢問一條路徑上的重邊數量。
\(1\leq T\leq 3,1\leq n,m\leq 10^5\)
解題思路
前隨便找個點當根,我們用每個點去儲存它連向它父節點邊的資訊。
然後考慮如何進行操作,發現是樹上的路徑操作,考慮樹鏈剖分。
*為了方便描述我們將樹鏈剖分的輕重邊用加粗進行描述
首先我們可以先把路徑上所有邊(所對應的點儲存的資訊)都改成重邊,那麼問題就出在我們如何把連線的重邊改成輕邊。
暴力修改這些邊顯然不可行,我們注意到樹鏈剖分後的我們可以方便的修改重邊,而一條路徑上的輕邊路徑不多,所以我們可以考慮只統一維護重邊資訊,而輕邊資訊我們可以在查詢的時候再處理。
那麼做法就很顯然了,對於重邊的資訊我們用線段樹修改,而對於輕邊,我們再開一個線段樹記錄每個端點上次被覆蓋的路徑編號。
如果輕邊所連線的兩個端點是被不同路徑覆蓋的,那麼這條邊就是輕邊,不然就是重邊。
時間複雜度:\(O(m\log^2 n)\)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cctype> using namespace std; const int N=1e5+10; int read(){ int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-f;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+c-48;c=getchar();} return x*f; } struct node{ int to,next; }a[N<<1]; int T,n,m,cnt,tot,ls[N],fa[N],dep[N]; int rfn[N],ed[N],siz[N],son[N],top[N],id[N]; void addl(int x,int y){ a[++tot].to=y; a[tot].next=ls[x]; ls[x]=tot;return; } void dfs(int x){ rfn[x]=++cnt;siz[x]=1; dep[x]=dep[fa[x]]+1; for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(y==fa[x])continue; fa[y]=x;dfs(y); siz[x]+=siz[y]; if(siz[y]>siz[son[x]])son[x]=y; } ed[x]=cnt; return; } void dFs(int x){ id[x]=++cnt; if(son[x]){ top[son[x]]=top[x]; dFs(son[x]); } for(int i=ls[x];i;i=a[i].next){ int y=a[i].to; if(y==fa[x]||y==son[x])continue; top[y]=y;dFs(y); } return; } struct SegTree{ int w[N<<2],lazy[N<<2]; void Clear(){ memset(w,0,sizeof(w)); memset(lazy,0,sizeof(lazy)); return; } void Downdata(int x,int l,int r){ if(!lazy[x])return;int mid=(l+r)>>1; w[x*2]=(mid-l+1)*lazy[x]; w[x*2+1]=(r-mid)*lazy[x]; lazy[x*2]=lazy[x*2+1]=lazy[x]; lazy[x]=0;return; } int Ask(int x,int L,int R,int l,int r){ if(L==l&&R==r)return w[x]; int mid=(L+R)>>1;Downdata(x,L,R); if(r<=mid)return Ask(x*2,L,mid,l,r); if(l>mid)return Ask(x*2+1,mid+1,R,l,r); return Ask(x*2,L,mid,l,mid)+Ask(x*2+1,mid+1,R,mid+1,r); } void Change(int x,int L,int R,int l,int r,int val){ if(L==l&&R==r){w[x]=(R-L+1)*val;lazy[x]=val;return;} int mid=(L+R)>>1;Downdata(x,L,R); if(r<=mid)Change(x*2,L,mid,l,r,val); else if(l>mid)Change(x*2+1,mid+1,R,l,r,val); else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val); w[x]=w[x*2]+w[x*2+1]; } }Tw,Tl; void Updata(int x,int y,int pos){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); if(top[x]!=x)Tw.Change(1,1,n,id[top[x]]+1,id[x],1); Tl.Change(1,1,n,id[top[x]],id[x],pos); if(son[x])Tw.Change(1,1,n,id[x]+1,id[x]+1,0); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); Tl.Change(1,1,n,id[x],id[y],pos); if(id[x]!=id[y])Tw.Change(1,1,n,id[x]+1,id[y],1); if(son[y])Tw.Change(1,1,n,id[y]+1,id[y]+1,0); if(top[x]!=x)Tw.Change(1,1,n,id[x],id[x],0); } bool check(int x){ int p=Tl.Ask(1,1,n,id[x],id[x]); if(!p)return 0; return (p==Tl.Ask(1,1,n,id[fa[x]],id[fa[x]])); } int Ask(int x,int y){ int ans=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); ans+=Tw.Ask(1,1,n,id[top[x]],id[x]); x=top[x];ans+=check(x); x=fa[x]; } if(dep[x]>dep[y])swap(x,y); if(id[x]!=id[y])ans+=Tw.Ask(1,1,n,id[x]+1,id[y]); return ans; } int main() { T=read(); while(T--){ tot=0; memset(ls,0,sizeof(ls)); memset(fa,0,sizeof(fa)); memset(son,0,sizeof(son)); Tl.Clear();Tw.Clear(); n=read();m=read(); for(int i=1;i<n;i++){ int x=read(),y=read(); addl(x,y);addl(y,x); } cnt=0;dfs(1);cnt=0; top[1]=1;dFs(1);cnt=0; while(m--){ int op=read(),x=read(),y=read(); if(op==1)++cnt,Updata(x,y,cnt); else cout<<Ask(x,y)<<'\n'; } } return 0; }