1. 程式人生 > 其它 >P7735-[NOI2021]輕重邊【樹鏈剖分,線段樹】

P7735-[NOI2021]輕重邊【樹鏈剖分,線段樹】

前言

之前線上賽就A的題現在才寫部落格


正題

題目連結:https://www.luogu.com.cn/problem/P7735


題目大意

\(n\)個點的一棵樹,開始所有邊都是輕邊,\(m\)次操作。

  1. \(x\rightarrow y\)路徑上所有點連線的重邊都變為輕邊,然後再把路徑上的邊變成重邊。
  2. 詢問一條路徑上的重邊數量。

\(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;
}