[ABC229E] Graph Destruction 题解

文章讲述了如何使用并查集数据结构解决一个题目,题目要求在特定条件下维护连通块数量。通过将删除操作转化为添加操作,作者详细描述了如何在每次加点时统计连通块数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ABC229E Graph Destruction

一道比较套路的题,没有太多的思维量,也没有考察很难的算法。

解法

看到题中有求连通块,我们就可以用并查集来维护连通块。又因为普通并查集只支持添加操作,不支持撤销操作,所以我们思考一个方向的做法。题中让我们正着删点,我们就倒着加点就可以了。现在条件就被我们转化为了如下 3 3 3 条:

  • 给定 n n n 个点, m m m 条边等待被添加,现在拥有一个空图。
  • 按照 n , n − 1 , … , 1 n,n-1,\dots,1 n,n1,,1 的顺序添加点及与其相连且另一个点的编号大于该点编号的边。
  • 对于每次加点操作,求出现在图中连通块的数量并记录,因为我们最后要正着输出。

具体执行

按照如上 3 3 3 条,我们可以按照如下思路统计连通块个数:

  • 如果在图外加了一个点,则连通块数量加 1 1 1
  • 加点后我们再加边,如果加完边沟通了两个不同的连通块,则连通块数量减 1 1 1

最后代码如下:

#include<bits/stdc++.h>
using namespace std;
#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<21,stdout),p3=Ouf),*p3++=c
char Inf[1<<21],Ouf[1<<21],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<21);
inline void read(int &x,char c=Getchar())
{
	bool f=c!='-';
	x=0;
	while(c<48 or c>57) c=Getchar(),f&=c!='-';
	while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();
	x=f?x:-x;
}
inline void write(int x)
{
	if(x<0) Putchar('-'),x=-x;
	if(x>=10) write(x/10),x%=10;
	Putchar(x^48);
}
int n,m,head[200010],cnt,fa[200010],ans[200010],tot;
struct edge
{
	int to,next;
};
edge e[400010];
inline void add(const int &x,const int &y)
{
	e[++cnt].to=y,e[cnt].next=head[x],head[x]=cnt;
}
inline int find(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}
int main()
{
	read(n),read(m);
	for(int i=1,x,y;i<=m;i++) read(x),read(y),add(x,y),add(y,x);
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=n;i;i--)
	{
		ans[i]=tot,tot++;
		for(int j=head[i];j;j=e[j].next)
			if(e[j].to>i && find(e[j].to)!=find(i)) fa[fa[e[j].to]]=fa[i],tot--;
	}
	for(int i=1;i<=n;i++) write(ans[i]),Putchar('\n');
	fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值