做题记录整理并查集2 P1197. [JSOI2008] 星球大战(2022/9/16)

P1197. [JSOI2008] 星球大战

一眼并查集

题目的难点在于如何处理这种每次删除一个点的·操作,实际上就是换个角度想:

我们不删除点,而是反过来,先不加入需要删除的点,之后再一个一个将点加回去,倒着做

这种换位思考的方式感觉很像从记录区间贡献变成记录点的贡献,或者廊桥分配那样的题目

#include <iostream>
#include <cstdio>
#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
#define ll long long
#define mp(a,b) make_pair(a,b)
using namespace std;
struct node{
	int from;
	int to;
	int nex;
}a[500005];
int hd[500005],cnt;
int jx,jy;
int vis[500005];
int ji[500005];
int ans[500005];
int n,m;
int fa[500005];
void ru(int x,int y)
{
	a[++cnt].to=y;
	a[cnt].from=x;
	a[cnt].nex=hd[x];
	hd[x]=cnt;
}
int zhao(int x)
{
    if(x == fa[x]) return x;
    return fa[x] = zhao(fa[x]);
}
int main() 
{
    cin>>n>>m;
        for1(i,0,n) fa[i] = i,hd[i]=-1;
	for1(i,1,m)
	{
	    scanf("%d %d",&jx,&jy);
		ru(jx,jy);
		ru(jy,jx);
	}
	
	
	int k;
	cin>>k;
	for1(i,1,k)
		scanf("%d",&ji[i]),vis[ji[i]]=1;
	int z=n-k;
	for1(i,1,m*2)
	{
		if(vis[a[i].from]!=1&&vis[a[i].to]!=1)
		{
		if(zhao(a[i].from)!=zhao(a[i].to))
			z--;
			fa[zhao(a[i].from)]=fa[zhao(a[i].to)];
		}
		
	}
	ans[k+1]=z;
	
	for(int i=k;i>=1;i--)
	{
		z++;//修复
		vis[ji[i]]=0;
		for(int j=hd[ji[i]];j!=-1;j=a[j].nex)
		{
			int v=a[j].to;
			if(vis[a[j].to]!=1&&zhao(ji[i])!=zhao(a[j].to))
	    	{
			z--;
			fa[zhao(ji[i])]=fa[zhao(a[j].to)];
		    }
		} 
		ans[i]=z;
	}
	for1(i,1,k+1) cout<<ans[i]<<endl;
	return 0;
}
posted @ 2022-09-16 19:24  yyx525jia  阅读(19)  评论(0)    收藏  举报