洛谷P4219 大融合(LCT、虚子树)

解析

本题需要用LCT维护子树大小
然后我就不会了
然后我就用树剖水过去了
又快又好写,真香

现在详细聊聊如何用LCT维护子树信息
每个结点再定义一个新的变量记录所有虚儿子的信息
然后…完了?

告别盲目pushup,我们来详细聊聊在哪里需要更新虚儿子的信息

毋庸置疑,应该在虚儿子信息改变的时候(废话
那么什么时候发生改变呢?

  1. access:虚实边会交换状态,需要更新虚子树状态
  2. link:增加了虚儿子,需要更新虚子树状态

没了
splay、rotate、makeroot、findroot都是在一个splay里自己玩泥巴,显然不会改变虚儿子的状态
注意cut是减少了一个实儿子,也没有改变虚儿子状态
似乎还不太难对吧
所以,对于LCT的标记问题,我们要理性分析,相信科学

update:

纸上得来终觉浅,绝知此事要躬行

(翻译:不要口胡)
qwq
不自己写一遍是真的找不到坑点…
这里的link改变虚子树状态的时候,会连带上面一串splay的信息都出问题!
而且由于其他地方默认的是原来的信息正确,因此这里即使后面splay或makeroot也于事无补
解决办法是把y转到根再更新其虚子树信息

inline void link(int x,int y){
	makeroot(x);access(y);splay(y);
	siz0[y]+=siz[x];f[x]=y;pushup(y);
	return;
}

代码

然而懒得重写,还是贴的我的树剖
明天晨练可以写遍这个

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+100;
const int mod=1e9+7;
const double eps=1e-9;
inline ll read() {
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}

int n,m;
struct node{
	int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){
	p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
	return;
}
struct ope{
	int op,x,y;
}q[N];
struct edge{
	int x,y;
}e[N];
int tot,fa[N],num[N];
int find(int x){return x==fa[x]?x:find(fa[x]);}
inline void merge(int x,int y){
	x=find(x);y=find(y);
	if(num[x]>num[y]) swap(x,y);
	e[++tot]=(edge){x,y};
	fa[x]=y;num[y]+=num[x];
	return;
}

int dfn[N],pos[N],tim,f[N],siz[N],hson[N],top[N];
int pl[N][19];
void dfs1(int x,int fa){
	f[x]=fa;
	pl[x][0]=fa;
	for(int k=1;pl[x][k-1];k++) pl[x][k]=pl[pl[x][k-1]][k-1];
	siz[x]=1;
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(to==fa) continue;
		dfs1(to,x);
		siz[x]+=siz[to];
		if(siz[to]>siz[hson[x]]) hson[x]=to;
	}
	return;
}
void dfs2(int x,int tp){
	top[x]=tp;
	pos[x]=++tim;dfn[tim]=x;
	if(hson[x]) dfs2(hson[x],tp);
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(to==f[x]||to==hson[x]) continue;
		dfs2(to,to);
	}
	return;
}

int val[N];
inline void add(int p,int w){
	for(;p<=n;p+=p&-p) val[p]+=w;
	return;
}
inline int ask(int p){
	int res(0);
	for(;p;p-=p&-p)	res+=val[p];
	return res;
}
void init(){
	for(int i=1;i<=n;i++){
		add(pos[i],siz[i]);
		add(pos[i]+1,-siz[i]);
	}
	return;
}

void upd(int x,int anc,int w){
	while(top[x]!=top[anc]){
		add(pos[top[x]],w);
		add(pos[x]+1,-w);
		x=f[top[x]];
	}
	add(pos[anc],w);
	add(pos[x]+1,-w);
	return;
}

ll ans[N];
int o;
int main() {
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	memset(fi,-1,sizeof(fi));cnt=-1;
	n=read();m=read();
	for(int i=1;i<=n;i++) num[i]=1,fa[i]=i;
	for(int i=1;i<=m;i++){
		char c;int x,y;
		scanf(" %c",&c);x=read(),y=read();
		q[i]=(ope){c=='A',x,y};
		if(c=='A'){
			addline(x,y);addline(y,x);
			merge(x,y);
		}
	}
	for(int i=1;i<=n;i++){
		if(!siz[i]){
			dfs1(i,0);dfs2(i,i);
		}
	}
	
	init();
	
	for(int i=m;i>=1;i--){
		if(q[i].op){
			int x=e[tot].x,y=e[tot].y;--tot;
			num[y]-=num[x];fa[x]=x;
			x=q[i].x,y=q[i].y;	
			if(f[x]==y) swap(x,y);
			int w=ask(pos[y]);
			int tp=x,oo=find(x);
			for(int k=17;k>=0;k--){
				if(!pl[tp][k]||find(pl[tp][k])!=oo) continue;
				tp=pl[tp][k];
			}
			upd(x,tp,-w);
			//printf("cut:(%d %d) w=%d tp=%d\n\n",x,y,w,tp);
		}
		else{
			int x=q[i].x,y=q[i].y;
			if(f[x]==y) swap(x,y);
			int sum=num[find(x)],bot=ask(pos[y]);
			ans[++o]=1ll*bot*(sum-bot);
			//printf("(%d %d):sum=%d bot=%d\n\n",x,y,sum,bot);
		}
	}
	while(o) printf("%lld\n",ans[o--]);
	return 0;
}
/*
8 12
A 2 3
Q 2 3
A 3 4
Q 2 3
A 3 8
Q 3 8
Q 3 4
Q 2 3
A 8 7
A 6 5
Q 8 7
Q 5 6

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值