广告位招租 C-City

题目描述

       若是城市A和城市B互通,城市B和城市C互通,那么城市A和城市C也互通,A、B、C三个城市算一个汇集点。先已知有n个城市和m条道路,想求的是有几个汇集点?但小S以为太简单了,因为战争缘由,某些城市会被导弹销毁掉,与之相应的道路也变得不可用。以前已经被销毁的不会被复原。现给定每次销毁的城市顺序,求每次销毁后汇集点有多少个。node

输入

第一行输入n和m,表示城市数量和道路数量(1≤n≤104,1≤m≤2n)(1≤n≤104,1≤m≤2n)ios

接下来m行,每行输入两个数ai和bi(1≤ai,bi≤n)(1≤ai,bi≤n)。表示ai和bi直接有道路c++

第m+2行输入q,表示有q个城市会被销毁 (1≤q≤n)(1≤q≤n)spa

接下来输入q个数,每行输入一个不重复的数,表示被销毁的城市code

输出

输出一行q个数,每i个数表示第i个城市销毁后汇集点的数量ci

样例输入

8 9
1 2
1 3
1 6
2 4
3 6
4 5
4 7
5 7
5 8
4
3 2 5 4

样例输出

1 2 3 3

 

并查集,经过储存节点反向建树,以后再一个个加点寻找集合个数it

 

#include <bits/stdc++.h>

using namespace std;

const int maxn=1e4+10;

struct node{
	int a,b;
	bool flag;//已经加过的点无须再加,经过flag进行标记
}node[maxn<<1];

int pre[maxn<<1];
int n,m,t;
vector<int> res;//储存须要删除的节点
stack<int> st;//储存集合个数
bool vis[maxn<<1];

int find(int x)
{
	return pre[x]=(pre[x]==x?x:find(pre[x]));//状态压缩
}

void unite(int x,int y)
{
	x=find(x),y=find(y);
	if(x>y)	pre[x]=y;//秩低向秩高的合并,防止树的退化
	else pre[y]=x;
}

int main()
{
	ios::sync_with_stdio(false);
	memset(vis,0,sizeof(vis));
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		pre[i]=i;
	for(int i=0;i<m;i++)
		{
			cin>>node[i].a>>node[i].b;
			node[i].flag=false;
		}
	cin>>t;
	for(int i=0;i<t;i++)
		{
			int tmp;
			cin>>tmp;
			vis[tmp]=true;//标记已经被删除的点
			res.push_back(tmp);
		}
	vector<int>::iterator it;
	/*it=res.end()-1;
	for(int i=0;i<m;i++)
		{
			if(!vis[node[i].a]&&!vis[node[i].b]&&!node[i].flag)
				{
					unite(node[i].a,node[i].b);
					node[i].flag=true;
					cout<<i<<endl;
				}
		}*/
	while(t--)
		{
			it=res.end()-1;
			for(int i=0;i<m;i++)
				{
					if(!vis[node[i].a]&&!vis[node[i].b]&&!node[i].flag)
						{
							unite(node[i].a,node[i].b);
							node[i].flag=true;
						}
				}
			int sum=0;
			for(int i=1;i<=n;i++)
				if(find(i)==i)	sum++;
			st.push(sum);
			vis[*it]=false;//加点
			res.erase(it);
		}
	int cnt=1;
	while(!st.empty())
		{
			cout<<st.top()-cnt++<<' ';
			st.pop();
		}
	cout<<endl;
	return 0;
}

应有更快的解法io