时间:
2022.4.28
题解:
并查集 + 思维。正向难以求解,所以把题目反向理解为,一边添加点,一边求连通块个数。
此时初始状态就是其他所有已经存在的点组成的图。根据 fa[i] == i 统计联通块的个数。
然后按照输入的逆序添加点,刚添加的点独立,所以是一个新的连通块,cnt++。
然后枚举这个点相连的所有点,因为要连边。在它相连的点之间,如果存在一个点已经在图中出现,且它们两个不在一个连通块中,那么他们相连就会导致连通块数减一,即 cnt- -。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl "\n"
#define inf 0x3f3f3f3f
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define IOS ios::sync_with_stdio(0); cin.tie(0)
const int N = 100 + 4e5;
int tot, head[N], fa[N];
struct node
{
int to, next, val;
}e[N<<1];
void add(int u, int v)
{
e[tot].to = v; e[tot].next = head[u]; head[u] = tot++;
}
int get(int x)
{
if(fa[x] == x) return x;
return fa[x] = get(fa[x]);
}
void merge(int a, int b)
{
int dx = get(a);
int dy = get(b);
if(dx != dy)
{
fa[dx] = dy;
}
}
void slove()
{
memset(head, -1, sizeof head);
int n, m; cin>>n>>m;
for(int i = 0; i < n; i++) fa[i] = i;
vector<vector<int> > g(n);
vector<int> u(m), v(m);
for(int i = 0; i < m; i++)
{
cin>>u[i]>>v[i];
add(u[i], v[i]); add(v[i], u[i]);
}
int k; cin>>k;
vector<int> cut(k);
int mp[N];
for(int i = 0; i < n; i++) mp[i] = 0;
for(int i = 0; i < k; i++)
{
cin>>cut[i];
mp[cut[i]] = 1;
}
for(int i = 0; i < m; i++)
{
if(!mp[u[i]] && !mp[v[i]])
{
merge(u[i], v[i]);
}
}
int cnt = 0;
for(int i = 0; i < n; i++)
{
if(get(i) == i && !mp[i]) cnt++;
}
vector<int> ans(n);
for(int i = k-1; i >= 0; i--)
{
mp[cut[i]] = 0;
ans[i] = cnt;
cnt++;
for(int j = head[cut[i]]; ~j; j = e[j].next)
{
int v = e[j].to;
if(!mp[v] && get(cut[i]) != get(v))
{
cnt--;
merge(cut[i], v);
}
}
}
cout<<cnt<<endl;
for(int i = 0; i < k; i++) cout<<ans[i]<<endl;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
IOS;
slove();
return 0;
}