C: City
时间限制: 1 s 内存限制: 128 MB
提交
题目描述
如果城市A和城市B互通,城市B和城市C互通,那么城市A和城市C也互通,A、B、C三个城市算一个聚集点。先已知有n个城市和m条道路,想求的是有几个聚集点?但小S觉得太简单了,由于战争原因,某些城市会被导弹销毁掉,与之相应的道路也变得不可用。之前已经被销毁的不会被复原。现给定每次销毁的城市顺序,求每次销毁后聚集点有多少个。
输入
第一行输入nn和mm,表示城市数量和道路数量(1≤n≤104,1≤m≤2n)(1≤n≤104,1≤m≤2n)
接下来mm行,每行输入两个数aiai和bibi (1≤ai,bi≤n)(1≤ai,bi≤n)。表示ai和bi直接有道路
第m+2m+2行输入qq,表示有qq个城市会被销毁 (1≤q≤n)(1≤q≤n)
接下来输入qq个数,每行输入一个不重复的数,表示被销毁的城市
输出
输出一行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<iostream>
#include<vector>
using namespace std;
vector<int>p[10011];
int city[10011],fa[10011],num[10011];
bool vis[10011];
int find(int x){
return fa[x]=(x==fa[x]?x:find(fa[x]));//并查集查找祖宗结点
}
int main(){
int n,m,q,x,y;
scanf("%d%d",&n,&m);
while(m--){
scanf("%d%d",&x,&y);
p[x].push_back(y);
p[y].push_back(x);
}
scanf("%d",&q);
for(int i=0;i<q;i++)
scanf("%d",&city[i]),vis[city[i]]=1;
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=n;i++)
if(!vis[i])
for(auto x:p[i])
if(!vis[x])
fa[find(x)]=find(i);
int ans=0;
for(int i=1;i<=n;i++)
if(!vis[i]&&find(i)==i)//祖宗为自己
ans++;
num[q-1]=ans;
for(int i=q-1;i>=0;i--){
vis[city[i]]=0;
ans++;
//未判断是否能合并在一个聚集点前建立一所城市看作是添加一个聚集点
for(auto x:p[city[i]])
if(!vis[x]&&find(city[i])!=find(x)){
//祖宗不一样但是连在一块,说明可以合并为1个聚集点
ans--;
fa[find(x)]=find(city[i]);//祖宗合并
}
num[i-1]=ans;
//记录建立该城市后的聚集点数目(摧毁该城市前的聚集点数目)
}
for(int i=0;i<q;i++)
printf("%d%s",num[i],i==q-1?"
":" ");
return 0;
}