【并查集】DZY loves graph

传送门

官方题解

参考博客

本题有个特殊的性质,就是加边的时候可以保证边是从小到大加的,因为操作编号是从小到大的。

 

那么先考虑加边。如果这朵边的两个端点已经在同一个集合中,就可以不管这条边,但是还是要把这条边存到栈里!!因为到时候删边的时候要用。

如果这坨边连接了两个不连通的集合,就用启发式合并把这两个集合合并。这样可以保证复杂度是对的。

如果是路径压缩的话可以存在操作使其复杂度为O(n),然后一直撤销鬼畜就炸了。

 

然后如果Delete操作在Return操作前面可以事先存下结果直接输出。

Delete一个节点p的时候把p的所有祖先的siz减掉,把p的父亲置为它自己就行了。

用ans[x]存储当前有x条边的答案。

use_edge[d]=-1表示不需要d这条边,因为d两端早已连通,而后来的边权一定要大一些,一定不会用到最小生成树上。

如果需要这条边,use_edge[d]则表示合并的时候siz小一些的集合的那个根。后面删边要用。

注意,tot表示实际有多少条边,cnt_edge表示最小生成树(可能还未连通)上有多少条边,删边的时候注意减的顺序。

然后删的边用一个栈来维护就好。要开longlong。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e5+10;
int n,m,a,b,k,fa[maxn],use_edge[maxn];
char op[maxn][10];int x[maxn],y[maxn],siz[maxn];
ll ans[maxn],cur;
stack<int> Q;
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
inline void print(ll x){
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
inline int getfa(int x){return (fa[x]==x)?(x):(getfa(fa[fa[fa[x]]]));}
inline void Delete(int p){for(int u=fa[p];fa[u]!=u;u=fa[u])siz[u]-=siz[p];fa[p]=p;}
inline void addedge(int x,int y,int d){
	int px=getfa(x),py=getfa(y);Q.push(d);
	if(px==py){use_edge[d]=-1;return;}
	if(siz[px]>siz[py]) swap(px,py);
	fa[px]=py,siz[py]+=siz[px],use_edge[d]=px;
}
inline void solve(){
	int tot=0,cnt_edge=0;
	for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
	for(int i=1;i<=m;++i){
		if(op[i][0]=='A'){
			addedge(x[i],y[i],i);
			if(use_edge[i]!=-1) cur+=i,cnt_edge++;
			ans[++tot]=(cnt_edge<n-1)?0:cur;
			print(ans[tot]),putchar('\n');
			if(op[i+1][0]=='R') op[i+1][0]='D',x[i+1]=1;
		}
		if(op[i][0]=='D'){
			if(op[i+1][0]=='R'){
				print(ans[tot-x[i]]),putchar('\n');
				print(ans[tot]),putchar('\n');
				continue;
			}
			while(x[i]--){
				int p=Q.top();Q.pop(),tot--;
				if(use_edge[p]==-1) continue;
				Delete(use_edge[p]),cur-=p,cnt_edge--;
			}
			ans[tot]=(cnt_edge<n-1)?0:cur;
			print(ans[tot]),putchar('\n');
		}
	}
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;++i){
		scanf("%s",op[i]);
		if(op[i][0]=='A') x[i]=read(),y[i]=read();
		if(op[i][0]=='D') x[i]=read();
	}solve();
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值