bzoj1015: [JSOI2008]星球大战starwar(并查集+倒推)

本文介绍了一道关于图论的问题,通过使用并查集解决动态修改图中顶点后连通分量的变化问题。文章详细解释了如何逆序处理操作以简化问题,并提供了完整的代码实现。

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

题目传送门

题目大意:

 n个点,m条边的无向图;k次操作,每次操作是删掉一个点e[i],求0~k次操作(k+1)次操作的过程中,图的联通块数量。

 (k+1次:是因为,0次操作表示,一个点都没炸的时候,先要算一次全图的联通块数量)。

思路分析:

 之前做过一道类似的蒸发学水,是在矩阵中完成基本相同的操作,这套题是拓展到了图上。

1、因为求联通块的数量,应该能想到是并查集(并查集就是一个两句话的函数,不懂请百度)

2、如果按题目思路的顺序来做的话,不知道怎么办(e xin);

3、逆序做的话,会变得浅显易懂。

4、初始化(算第一个情况的时候方法很多,本来我用深搜的,但是看了 hanks_o神的思路,用了更简单的方法)要注意一下。

5、题目的点是(0~n-1,我用1~n)其他的没有复杂的地方,代码有注解~

d

上代码:

#include<cstdio>
const int mx=400005;
struct nodx{int h,v;}a[mx];//点 
struct nodb{int x,y,gg;}b[mx*2];//边 
int n,m,f[mx],e[mx],su[mx],ans=0,len=0;
//f数组用于并查集 
//e数组用来记录破坏的点的顺序;su数组记录答案 
void ins(int x,int y)//邻接表 
{ len++;b[len].x=x;b[len].y=y;b[len].gg=a[x].h;a[x].h=len; }
int ch(int x) //并查集 
{ if(x==f[x]) return x; return f[x]=ch(f[x]);}
int main()
{
	scanf("%d %d",&n,&m); int x,y,tx,ty;
	for(int i=1;i<=n;i++) { f[i]=i;a[i].v=a[i].h=0; }//并查集的初始化是基础 
	for(int i=1;i<=m;i++)//构图 
	{
		scanf("%d %d",&x,&y);
		x++;y++;ins(x,y);ins(y,x);
	}
	scanf("%d",&m);//删点的个数 
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&e[i]);e[i]++;a[e[i]].v=1;//删掉的点,v值为 1 
	}
	//↓↓↓↓↓初始化部分
	 
	ans=n-m;//在没做任何操作之前,因为删掉了m个点,剩下(n-m)个集 
	for(int i=1;i<=len;i++)//扫描边,把剩下点合并,就算出来最后的集合数量 
	{
		x=b[i].x;y=b[i].y;
		if(a[x].v==0&&a[y].v==0)//点为被删除 
		{
			tx=ch(x); ty=ch(y);
			if(tx!=ty) //原来未连接 
			{
				f[tx]=ty; ans--;//连接,少了一个集合,ans-1 
			}
		}
	}
	//↑↑↑↑↑初始化部分 
	su[m+1]=ans;//(m+1)表示删掉m个点之后的状态 
	for(int k=m;k>=1;k--)//删掉 m-1~0个点的状态,注意是倒推的 
	{
		a[e[k]].v=0; x=e[k]; tx=ch(x); ans++;
		for(int i=a[x].h;i>0;i=b[i].gg)
		{
			int y=b[i].y;
			if(a[y].v==0)
			{
				ty=ch(y);//并查集是一个类似离线的算法,每次查找都要更新 
				if(tx!=ty)
				{
					f[ty]=tx;ans--;//注意这里的挂靠方向 
				}//如果写成 f[tx]=ty; 是错的~~~~思考一下? 
			}
		}
		su[k]=ans;//每次存答案 
	}
	for(int i=1;i<=m+1;i++) printf("%d\n",su[i]);//顺序输出 
	return 0;	
}



sdf

sdf

sdf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值