一道并查集的好题目
归纳题意就是给出一张图
支持两种操作
(1)删除一个点
(2)询问两个点是否联通
首先,询问两个点是否联通可以很容易地想到并查集
其次,对于依次删除图中的结点,启发我们将顺序删除操作变为逆序添加操作进行处理
最后,由于是删除一部分点,所以要把边先存一下,删完点做并集,每次加点也要做并集
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 5e4 + 10;
const int M = 4e5 + 10;
const int D = 1e3 + 10;
struct node
{
int x,y;
}a[M];
int root[N],b[D],ans[D];
bool vis[N];
vector<node> c[D];
int head[N],e[M],ne[M],idx;
void add(int a,int b)
{
idx ++;
e[idx] = b;
ne[idx] = head[a];
head[a] = idx;
}
int find(int x)
{
if(root[x] != x)
root[x] = find(root[x]);
return root[x];
}
int main()
{
int n,m,d;
cin >> n >> m >> d;
memset(head,-1,sizeof(head));
for(int i = 0;i < m;i ++)
{
int x,y;
scanf("%d %d",&x,&y);
a[i] = {x,y};
add(x,y),add(y,x);
}
memset(vis,true,sizeof(vis));
for(int i = 1;i <= d;i ++)
{
int q;
cin >> b[i] >> q;
vis[b[i]] = false;
while(q --)
{
int x,y;
cin >> x >> y;
c[i].push_back({x,y});
}
}
for(int i = 1;i <= n;i ++)
root[i] = i;
for(int i = 0;i < m;i ++)
if(vis[a[i].x] && vis[a[i].y])
{
int rootx = find(a[i].x),rooty = find(a[i].y);
root[rooty] = rootx;
}
for(int i = d;i;i --)
{
int res = 0;
for(int j = 0;j < c[i].size();j ++)
if(find(c[i][j].x) != find(c[i][j].y))
res ++;
ans[i] = res;
vis[b[i]] = true;
for(int j = head[b[i]];~ j;j = ne[j])
{
int k = e[j];
if(vis[k])
{
int roota = find(b[i]),rootb = find(k);
root[rootb] = roota;
}
}
}
for(int i = 1;i <= d;i ++)
cout << ans[i] << endl;
return 0;
}