洛谷P2056 [ZJOI2007]捉迷藏

題目描述

題解

聽說是邊分治板子題所以來補下坑。

其實第一眼看到題目我的想法是一條邊只有當兩端都是關閉的時候纔是有效邊,於是就可以線段樹分治,然後用可持久化並查集維護直徑,應該也是對的吧(沒寫不知道),but我是來補坑的。

所以來講講邊分治是啥,就像點分治一樣,是用來解決跟鏈有關的東西,區別就是邊分治的每個節點是邊。然後像點分治那樣去解決即可,但如果一張圖是菊花圖的話會被卡,所以我們要重建一下圖,大概就像下圖這樣把兒子分散開來。

這樣整棵樹就變成二叉的了,因此我們總能找到一條邊使得這條邊兩側的邊的差不會超過 11 ,這樣就能保證邊分樹層數是 O(logn)O(logn) 的。然後每次修改一個點的話就在邊分樹上跳即可。

對於這題來說我們可以在每條邊上用堆維護左右子樹內的點到這條邊的最大距離即可,修改一次次數不會超過 O(logn)O(logn) ,故效率爲 O(Qlog2n)O(Qlog^2n)

代碼

#include <bits/stdc++.h>
using namespace std;
const int N=4e5+5;
int n,hd[N],V[N],W[N],nx[N],sz[N],ax[N];
int ls[N],rs[N],t=1,o,rt,su,is[N],m,q;
char ch[3];bool vis[N];
vector<int>e[N];struct O{int u,d;};
bool operator < (O A,O B){return A.d<B.d;}
priority_queue<O>s[N][2];
struct P{int k,l,d;};vector<P>p[N];
void Add(int u,int v){e[u].push_back(v);}
void add(int u,int v,int w){
	nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;
}
void rebuild(int u,int fr){
	int x=0,z=e[u].size();is[u]=1;
	for (int v,y,i=0;i<z;i++){
		if ((v=e[u][i])==fr) continue;
		if (!x) add(u,v,1),add(v,u,1),x=u;
		else y=++n,add(x,y,0),add(y,x,0),
			add(y,v,1),add(v,y,1),x=y;
		rebuild(v,u);
	}
}
void Sz(int u,int fr){
	sz[u]=1;
	for (int i=hd[u];i;i=nx[i])
		if (V[i]!=fr && !vis[i>>1])
			Sz(V[i],u),sz[u]+=sz[V[i]];
}
void Rt(int u,int fr){
	for (int i=hd[u],v;i;i=nx[i])
		if (V[i]!=fr && !vis[i>>1]){
			Rt(V[i],u);
			v=max(sz[V[i]],o-sz[V[i]]);
			if (v<su) rt=i,su=v;
		}
}
void find(int u,int fr,int d,int k,int l){
	if (is[u]) s[k][l].push((O){u,d}),
		p[u].push_back((P){k,l,d});
	for (int i=hd[u];i;i=nx[i])
		if (V[i]!=fr && !vis[i>>1])
			find(V[i],u,d+W[i],k,l);
}
void upd(int k){
	for (int i=0;i<2;i++)
		while(!s[k][i].empty() && !is[s[k][i].top().u]) s[k][i].pop();
	if (s[k][0].empty() || s[k][1].empty()) ax[k]=0;
	else ax[k]=s[k][0].top().d+s[k][1].top().d+W[k<<1];
	ax[k]=max(ax[k],max(ax[ls[k]],ax[rs[k]]));
}
int ediv(int u){
	Sz(u,0);o=sz[u];su=1e9;rt=0;
	Rt(u,0);if (!rt) return 0;
	int x=V[rt],y=V[rt^1],z=rt>>1;vis[z]=1;
	find(x,0,0,z,0);find(y,0,0,z,1);if (!t) t=z;
	ls[z]=ediv(x);rs[z]=ediv(y);upd(z);return z;
}
void setbl(int u){
	int z=p[u].size();
	for (int i=z-1;~i;i--){
		P v=p[u][i];
		s[v.k][v.l].push((O){u,v.d});
		upd(v.k);
	}
}
void setwh(int u){
	int z=p[u].size();
	for (int i=z-1;~i;i--) upd(p[u][i].k);
}
int main(){
	cin>>n;m=n;
	for (int u,v,i=1;i<n;i++)
		scanf("%d%d",&u,&v),
		Add(u,v),Add(v,u);
	rebuild(1,0);t=0;ediv(1);cin>>q;
	for (int x;q--;){
		scanf("%s",ch);
		if (ch[0]=='G'){
			if (!m) puts("-1");
			else if (m<2) puts("0");
			else printf("%d\n",ax[t]);
		}
		else{
			scanf("%d",&x);is[x]^=1;
			if (is[x]) m++,setbl(x);
			else m--,setwh(x);
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章