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

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;
}
posted @ 2021-10-14 22:42  tzc_wk  阅读(44)  评论(0)    收藏  举报