nflsoj 20034 #12456. 「NOIP2021模擬賽0929知臨」連通塊
奆 \(\mathscr{L}\) 有一棵 \(n\) 個節點的樹,他現在要對這棵樹做 \(m\) 次操作,每次操作如下
刪除樹上的一條邊
查詢在節點 \(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\)
所以,必然可以得出新的樹的直徑的端點必然為原來兩個樹的直徑 \(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?*/