1. 程式人生 > 其它 >nflsoj 20034 #12456. 「NOIP2021模擬賽0929知臨」連通塊

nflsoj 20034 #12456. 「NOIP2021模擬賽0929知臨」連通塊

\(\mathscr{L}\) 有一棵 \(n\) 個節點的樹,他現在要對這棵樹做 \(m\) 次操作,每次操作如下

  1. 刪除樹上的一條邊

  2. 查詢在節點 \(u\) 目前所在的連通塊內,離 \(u\) 最遠的點的距離

兩點之間的距離定義為兩點之間簡單路徑的邊數

\(1\leq n,m\leq 2\cdot 10^5\)

時限 230ms

注意到時限非常不同尋常,聽說是卡 LCT .

首先,刪除操作不太好考慮,考慮倒過來合併 ,可以用個 dsu 維護一下 .

但是,這樣之只能維護有多少個連通塊,怎麼完成查詢操作呢?

\(u\) 最遠的點的距離可以想到樹的直徑,離每個樹上每個點最遠的距離必定在是樹直徑的兩個端點中的一個 .

那麼,可以考慮維護每個聯通塊的直徑 .

現在唯一要考慮的問題就是如何求出合併後的樹的直徑呢?

發現,新的樹的直徑的端點必然為原來兩個樹的直徑 \(4\) 個端點中的 \(2\) 個 .

考慮反證法,設新連的邊為 \((u,v)\),如果存在兩個節點 \(x\) , \(y\) 的距離大於上述得到的距離 ,其中 \(x\)\(y\) 不為兩個直徑的端點 . 有情況,

\(x\) , \(y\) 在同一個樹中,那麼,\(x,y\) 必然為此樹上的直徑,矛盾 .

\(x,y\) 在不同樹中,\(x\)\(u\) 所在的樹中, \(y\)\(v\) 所在的樹中,其中,\(x\)\(u\)

的距離必然是 \(u\) 所在樹中最大的, \(y\)\(v\) 的距離必然是 \(v\) 所在的樹中最大的 . 只用考慮一遍,如果 \(x\) 不是樹的直徑,數直徑的兩個端點為 \(a,b\),那麼,\(dis(x,u)+dis(u,a)>dis(u,a)+dis(u,b)\) ,可以考慮把 \(a\) 替換成 \(u\) ,得到一個更大的直徑,與假設矛盾 (其實這也是上面 “離每個樹上每個點最遠的距離必定在是樹直徑的兩個端點中的一個” 的證明 ) .

所以,必然可以得出新的樹的直徑的端點必然為原來兩個樹的直徑 \(4\) 個端點中的 \(2\) 個這個結論.

因此,只需要兩兩端點求距離,比較一下就可以得到最大值 .

這個求距離需要放在原樹上求,而且需要寫一個 lca .

時間複雜度 : \(\mathrm O (n\log n)\)

空間複雜度 : \(\mathrm O (n)\)

code

#pragma GCC optimize ("Ofast")
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	int res=0;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return res;
}
inline void print(int res){
	if(res==0){
		putchar('0');
		return;
	}
	int a[10],len=0;
	while(res>0){
		a[len++]=res%10;
		res/=10;
	}
	for(int i=len-1;i>=0;i--)
		putchar(a[i]+'0');
}
int n,m;
vector<int>nei[200010];
int p[200010][20],dep[200010];
void dfs(int x,int fa){
	p[x][0]=fa;
	for(int i=0;i<(int)nei[x].size();i++){
		int to=nei[x][i];
		if(to==fa)continue;
		dep[to]=dep[x]+1;
		dfs(to,x);
	}
}
void build(){
	for(int k=0;k+1<20;k++){
		for(int i=0;i<n;i++){
			if(p[i][k]==-1)p[i][k+1]=p[i][k];
			else p[i][k+1]=p[p[i][k]][k];
		}
	}
}
int lca(int u,int v){
	if(dep[u]>dep[v])swap(u,v);
	for(int k=0;k<20;k++)if((dep[u]-dep[v])>>k&1)v=p[v][k];
	if(u==v)return u;
	for(int k=19;k>=0;k--){
		if(p[u][k]!=p[v][k]){
			u=p[u][k];
			v=p[v][k];
		}
	}
	return p[u][0];
}
inline int get_dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
bool ok[200010];
vector<pair<int,int> >e;
vector<pair<int,int> >q;
class node{public:int fa,x,y,dis;}f[200010];
inline int get_fa(int x){
	return f[x].fa==x?x:f[x].fa=get_fa(f[x].fa);
}
void merge(int u,int v){
	int ou=u,ov=v;
	u=get_fa(u);v=get_fa(v);
	if(u==v)return;
	f[v].fa=u;
	int dis=0,a,b,disa,disb,nx,ny;
	int d1,d2;
	d1=get_dis(f[u].x,ou);
	d2=get_dis(f[u].y,ou);
	a=d1>d2?f[u].x:f[u].y;
	disa=d1>d2?d1:d2;
	d1=get_dis(f[v].x,ov);
	d2=get_dis(f[v].y,ov);
	b=d1>d2?f[v].x:f[v].y;
	disb=d1>d2?d1:d2;
	if(disa+disb+1>dis){
		dis=disa+disb+1;
		nx=a;
		ny=b;
	}
	if(f[u].dis>dis){
		dis=f[u].dis;
		nx=f[u].x;
		ny=f[u].y;
	}
	if(f[v].dis>dis){
		dis=f[v].dis;
		nx=f[v].x;
		ny=f[v].y;
	}
	f[u].x=nx;
	f[u].y=ny;
	f[u].dis=dis;
}
int main(){
	freopen("block.in","r",stdin);
	freopen("block.out","w",stdout);
	n=read();m=read();
	for(int i=0;i<n-1;i++){
		int u=read()-1,v=read()-1;
		nei[u].push_back(v);
		nei[v].push_back(u);
		e.push_back(make_pair(u,v));
	}
	dfs(0,-1);
	build();
	for(int i=0;i<m;i++){
		int op=read(),x=read()-1;
		q.push_back(make_pair(op,x));
	}
	reverse(q.begin(),q.end());
	memset(ok,true,sizeof(ok));
	for(int i=0;i<(int)q.size();i++){
		int op=q[i].first,id=q[i].second;
		if(op==1)ok[id]=false;
	}
	for(int i=0;i<n;i++){
		f[i].fa=f[i].x=f[i].y=i;
		f[i].dis=0;
	}
	for(int i=0;i<(int)e.size();i++)if(ok[i]){
		int u=e[i].first,v=e[i].second;
		merge(u,v);
	}
	vector<int>ans;
	for(int t=0;t<(int)q.size();t++){
		int op=q[t].first;
		if(op==1){
			int id=q[t].second;
			int u=e[id].first,v=e[id].second;
			merge(u,v);
		}
		else{
			int x=q[t].second;
			ans.push_back(max(get_dis(x,f[get_fa(x)].x),get_dis(x,f[get_fa(x)].y)));
		}
	}
	reverse(ans.begin(),ans.end());
	for(int i=0;i<(int)ans.size();i++){
		print(ans[i]);
		putchar('\n');
	}
	return 0;
}
/*inline? ll or int? size? min max?*/