P2056 [ZJOI2007] 捉迷藏 【动态点分治】

题面


SOL

树的直径? 如果有 ∑ ∣ S ∣ ≤ 1 e 6 \sum|S|\le1e6 S1e6 之类的,说不定可以虚树做。然鹅并没有。

另一种 O ( n l o g n ) O(nlogn) O(nlogn)讨论完树中所有路径的做法当然是点分治了。

只是此题需要支持修改,于是记录一下点分治时候的 f a fa fa,每个分治重心维护一个数据结构,我们便有了一个 O ( n l o g n + m l o g 2 n ) O(nlogn+mlog^2n) O(nlogn+mlog2n)的AC算法,点分树(就是动态点分治。。)

具体的,每一个点维护两个堆,一个是每个分治子树该点的最长路径,一个是每一个分治子树中的所有路径,

怎么维护第二个堆? 子树不好找,但是父亲好找呀。可以维护用当前点对应父亲的第二个堆嘛。修改的时候就从当前点看父亲。

PS.这题有点卡常。。最开始居然把分治分错了,难怪TLE。。最后一个点开O2才过


CODE

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
#define cs const
#define ri register int
#define db double
#define lb long double
#define gc getchar()
#define in red()
inline int red(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=num*10+(c^48);
	return num*f;
}
cs int N=1e5+10,M=2e5+10;
int head[N],cnt=0,nxt[M],to[M];
inline void adde(int u,int v){
	nxt[++cnt]=head[u];head[u]=cnt;to[cnt]=v;
	nxt[++cnt]=head[v];head[v]=cnt;to[cnt]=u;
}
struct heap{
	priority_queue<int> a,b;
	inline void push(int x){a.push(x);}
	inline void pop(int x){if(b.size()<a.size())b.push(x);}
	inline int size(){return a.size()-b.size();}
	inline int top(){
		while(b.size()&&a.top()==b.top())a.pop(),b.pop();
		return a.size() ? a.top() : 0;
	}
	inline int mx(){
		int tmp=top();pop(tmp);
		int tmp2=top();
		return push(tmp),tmp+tmp2;
	}
}A,B[N],C[N];
int rt,mx[N],maxn,siz[N],fa[N],dfn[N],st[20][N<<1],Log[N<<1],tot,dep[N],n,m;
bool vis[N],col[N];
void dfs(int u,int f){
	st[0][++tot]=u;dfn[u]=tot;dep[u]=dep[f]+1;
	for(ri i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(v==f)continue;
		dfs(v,u);
		st[0][++tot]=u;
	}
}
inline int _min(int a,int b){return dfn[a]<dfn[b] ? a : b;}
inline void init(){
	dfs(1,0);Log[1]=0;	
	for(ri i=2;i<=tot;++i)Log[i]=Log[i>>1]+1;
	for(ri i=1;i<=Log[tot];++i){
		for(ri j=1;j+(1<<i)-1<=tot;++j){
			st[i][j]=_min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
		}
	}
}
inline int qy(int x,int y){
	int fx=min(dfn[x],dfn[y]),fy=max(dfn[x],dfn[y]),k=Log[fy-fx+1];
	return _min(st[k][fx],st[k][fy-(1<<k)+1]);
}
inline int dist(int x,int y){
	return dep[x]+dep[y]-2*dep[qy(x,y)];
}
inline void getrt(int u,int f){
	siz[u]=1;mx[u]=0;
	for(ri i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(v==f||vis[v])continue;
		getrt(v,u);
		siz[u]+=siz[v];
		mx[u]=max(mx[u],siz[v]);
	}
	mx[u]=max(mx[u],maxn-siz[u]);
	if(mx[u]<mx[rt])rt=u;
}
inline void divi(int u){
	vis[u]=1;
	for(ri i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(vis[v])continue;
		maxn=siz[v];rt=0;
		getrt(v,u);
		fa[rt]=u;
		divi(rt);
	}
}
inline void B_insert(heap &t,int k){
	if(t.size()==0)t.push(k);
	else if(t.size()==1)t.push(k),A.push(t.mx());
	else{
		int las=t.mx();t.push(k);
		int now=t.mx();
		if(las<now)A.pop(las),A.push(now);
	}
}
inline void B_delet(heap &t,int k){
	if(t.size()==1)t.pop(k);
	else if(t.size()==2){
		A.pop(t.mx());t.pop(k);
	}
	else{
		int las=t.mx();t.pop(k);
		int now=t.mx();
		if(las>now)A.pop(las),A.push(now);
	}
}
inline void C_insert(heap &t,heap &q,int k){
	if(!t.size())t.push(k),B_insert(q,k);
	else{
		int las=t.top();t.push(k);
		if(las<k)B_delet(q,las),B_insert(q,k);
	}
}
inline void C_delet(heap &t,heap &q,int k){
	if(t.size()==1)t.pop(k),B_delet(q,k);
	else{
		t.pop(k);
		int now=t.top();
		if(k>now)B_delet(q,k),B_insert(q,now);
	}
}	
inline void delet(int u,int v){
	if(u==v){
		if(B[u].size()==2)A.pop(B[u].mx());
		B[u].pop(0);
	}
	if(!fa[u])return;
	int f=fa[u],k=dist(f,v);
	C_delet(C[u],B[f],k);
	delet(f,v);
}
inline void add(int u,int v){
	if(u==v){
		B[u].push(0);
		if(B[u].size()==2)A.push(B[u].mx());
	}
	if(!fa[u])return;
	int f=fa[u],k=dist(f,v);
	C_insert(C[u],B[f],k);
	add(f,v);
}
char s;
signed main(){
//	freopen("data.in","r",stdin);
	n=in;
	for(ri i=1;i<n;++i){
		int u=in,v=in;
		adde(u,v);
	}
	init();
	maxn=n;rt=0;mx[0]=1e9;
	getrt(1,0);
	divi(rt);
	for(ri i=1;i<=n;++i)add(i,i),col[i]=1;
	m=in;
	int all=n;
	while(m--){
		s=gc;
		if(s=='G'){
			if(all<2)cout<<all-1<<'\n';
			else cout<<A.top()<<'\n';
			gc;	
		}
		if(s=='C'){
			int k=in;
			col[k]^=1;
			if(col[k])add(k,k),++all;
			else delet(k,k),--all;
		}
	}
	return 0;
}





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值