广告位招租 C-City
题目描述
如果城市A和城市B互通,城市B和城市C互通,那么城市A和城市C也互通,A、B、C三个城市算一个聚集点。先已知有n个城市和m条道路,想求的是有几个聚集点?但小S觉得太简单了,由于战争原因,某些城市会被导弹销毁掉,与之相应的道路也变得不可用。之前已经被销毁的不会被复原。现给定每次销毁的城市顺序,求每次销毁后聚集点有多少个。
输入
第一行输入n和m,表示城市数量和道路数量(1≤n≤104,1≤m≤2n)(1≤n≤104,1≤m≤2n)
接下来m行,每行输入两个数ai和bi(1≤ai,bi≤n)(1≤ai,bi≤n)。表示ai和bi直接有道路
第m+2行输入q,表示有q个城市会被销毁 (1≤q≤n)(1≤q≤n)
接下来输入q个数,每行输入一个不重复的数,表示被销毁的城市
输出
输出一行q个数,每i个数表示第i个城市销毁后聚集点的数量
样例输入
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
并查集,通过储存节点反向建树,之后再一个个加点寻找集合个数
#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;
}
应有更快的解法