1. 程式人生 > 其它 >Codeforces 1137F - Matches Are Not a Child's Play(LCT)

Codeforces 1137F - Matches Are Not a Child's Play(LCT)

LCT 維護類 access 操作,《樹點染色模型》(

Codeforces 題面傳送門 & 洛谷題面傳送門

考慮將一個點 \(x\) 的編號變為當前所有點編號最大值 \(+1\) 會對每個點的刪除時間產生怎麼樣的影響。由於編號最大的點肯定是最後一個被刪除的,因此我們不妨令編號最大的點為根,那麼可以發現,對於不在 \(x\) 到根這條路徑上的點,它們刪除的相對位置順序是不會發生變化的,因為刪除這樣的點時,肯定它們的兒子已經被刪除了,而它的父親肯定沒被刪除,因此 \(x\) 到根節點這條路徑上的點的刪除順序肯定不影響其它點的刪除順序,而由於此時 \(x\) 和原來的根節點 \(r\) 分別是所有點中,權值最大和第二大的點,因此 \(x\to r\)

中的點肯定是最後一批被刪除的,並且刪除順序取決於它們與 \(r\) 的遠近。

思考如何維護這個東西,發現這東西有點類似於 P3703 樹點染色 的模型,修改一個點點權時,相當於將這個點先 access 一遍把 \(x\to r\) 這條鏈搞出來並賦成一個新的顏色,然後再 makeroot(x)。因此我們嘗試用 LCT 維護這個東西,對於每個實鏈,我們給它一個優先值 \(v\),優先值越大表示這條鏈上的點越靠後被刪除,而同一條實鏈上的點的刪除順序則是按照深度的增加而遞減,因此考慮建一個 BIT,對於每種優先順序 \(v\) 維護當前有多少個點的優先順序為 \(v\),這樣查詢就做一遍字首和,減掉當前實鏈中深度比當前節點小的點數,這個可以通過 LCT 的 \(siz\)

維護(注意,這裡不需要寫 top tree,因為是維護鏈的資訊)。BIT 資訊的維護就按照套路在 LCT 時扣掉除了右子樹之外部分原來優先順序的貢獻,加上新貢獻即可。

時間複雜度 \(n\log^2n\),別看 LCT 常數大,加上個 BIT 的小常數後倒跑得飛快(

const int MAXN=2e5;
int n,qu,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0,deg[MAXN+5];
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int t[MAXN*2+5],col_cnt=0;
void add(int x,int v){for(int i=x;i<=MAXN*2;i+=(i&(-i))) t[i]+=v;}
int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;}
struct node{int ch[2],f,siz,rev_lz,col,col_lz;} s[MAXN+5];
void pushup(int k){s[k].siz=s[s[k].ch[0]].siz+s[s[k].ch[1]].siz+1;}
int ident(int k){return ((s[s[k].f].ch[0]==k)?0:((s[s[k].f].ch[1]==k)?1:-1));}
void connect(int k,int f,int op){s[k].f=f;if(~op) s[f].ch[op]=k;}
void rotate(int x){
	int y=s[x].f,z=s[y].f,dx=ident(x),dy=ident(y);
	connect(s[x].ch[dx^1],y,dx);connect(y,x,dx^1);connect(x,z,dy);
	pushup(y);pushup(x);
}
void tag_rev(int k){swap(s[k].ch[0],s[k].ch[1]);s[k].rev_lz^=1;}
void tag_col(int k,int v){s[k].col_lz=v;s[k].col=v;}
void pushdown(int k){
	if(s[k].rev_lz){
		if(s[k].ch[0]) tag_rev(s[k].ch[0]);
		if(s[k].ch[1]) tag_rev(s[k].ch[1]);
		s[k].rev_lz=0;
	} if(s[k].col_lz){
		if(s[k].ch[0]) tag_col(s[k].ch[0],s[k].col_lz);
		if(s[k].ch[1]) tag_col(s[k].ch[1],s[k].col_lz);
		s[k].col_lz=0;
	}
}
void pushall(int k){if(~ident(k)) pushall(s[k].f);pushdown(k);}
void splay(int k){
	pushall(k);
	while(~ident(k)){
		if(ident(s[k].f)==-1) rotate(k);
		else if(ident(s[k].f)==ident(k)) rotate(s[k].f),rotate(k);
		else rotate(k),rotate(k);
	}
}
void access(int k){
	int pre=0;
	for(;k;pre=k,k=s[k].f){
		splay(k);
		add(s[k].col,-s[k].siz+s[s[k].ch[1]].siz);
		add(col_cnt,s[k].siz-s[s[k].ch[1]].siz);
		s[k].ch[1]=pre;pushup(k);
	}
}
void makeroot(int k){
	col_cnt++;access(k);splay(k);
	tag_rev(k);tag_col(k,col_cnt);
}
int ask(int k){splay(k);return query(s[k].col)-s[s[k].ch[0]].siz;}
void dfs0(int x,int f){
	s[x].f=f;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];if(y==f) continue;
		dfs0(y,x);
	}
}
void calc(){
	priority_queue<int,vector<int>,greater<int> > q;
	for(int i=1;i<=n;i++) if(deg[i]==1) q.push(i);
	while(!q.empty()){
		int x=q.top();q.pop();col_cnt++;
		add(col_cnt,1);s[x].col=col_cnt;
		for(int e=hd[x];e;e=nxt[e]){
			int y=to[e];
			if((--deg[y])==1) q.push(y);
		}
	}
}
int main(){
	scanf("%d%d",&n,&qu);for(int i=1;i<=n;i++) s[i].siz=1;
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),deg[u]++,deg[v]++,adde(u,v),adde(v,u);
	dfs0(n,0);calc();
	while(qu--){
		static char opt[13];scanf("%s",opt+1);
		if(opt[1]=='u'){int x;scanf("%d",&x);makeroot(x);}
		else if(opt[1]=='w'){int x;scanf("%d",&x);printf("%d\n",ask(x));}
		else{
			int u,v;scanf("%d%d",&u,&v);
			printf("%d\n",(ask(u)<ask(v))?u:v);
		}
//		for(int i=1;i<=n;i++) printf("%d%c",ask(i)," \n"[i==n]);
	}
	return 0;
}