【BZOJ1015】【tyvj3487】星球大战starwar,特别的并查集技巧

传送门1
传送门2
写在前面:无
思路:比较简单的并查集题目,正着做很难,我们可以倒着想,询问倒着排,相当于每次加入一个星球并把可以连的边都连上,查一下联通块数量就行了
注意:必须两个顶点都没有被摧毁才能进行并查集操作
代码:

#include"bits/stdc++.h"
using namespace std;
int tot=1,x,y,n,m,k,ans;
struct os
{
    int u,v,next;
}a[400010];
int father[400010],first[400010],q[400010],outs[400010];
bool flag[400010];
int in()
{
    int t=0;
    char ch=getchar();
    while (ch>'9'||ch<'0') ch=getchar();
    while (ch>='0'&&ch<='9') t=t*10+ch-'0',ch=getchar();
    return t;
}
void add(int x,int y)
{
    a[++tot].u=x;
    a[tot].v=y;
    a[tot].next=first[x];
    first[x]=tot;
}
int find(int x)
{
    if (x!=father[x]) father[x]=find(father[x]);
    return father[x];
}
void unions(int x,int y)
{
    if (x!=y) father[x]=y,ans--;
}
main()
{
    n=in();m=in();
    for (int i=1;i<=m;i++)
    x=in(),y=in(),
    add(x,y),add(y,x);
    k=in();
    ans=n;
    for (int i=1;i<=k;i++)
    {
        q[i]=in();
        ans--;
        flag[q[i]]=1;
    }
    for (int i=1;i<=n;i++) father[i]=i; 
    for (int i=1;i<=n;i++)
    if (!flag[i])
    for (int j=first[i];j;j=a[j].next)
    if (!flag[a[j].v])
    unions(find(a[j].v),find(a[j].u));
    for (int i=k;i>=1;i--)
    {
        outs[i]=ans++;
        flag[q[i]]=0;
        for (int j=first[q[i]];j;j=a[j].next)
        if (!flag[a[j].v])
        unions(find(a[j].v),find(a[j].u));
    }
    printf("%d\n",ans);
    for (int i=1;i<=k;i++)
    printf("%d\n",outs[i]);
}

你可能感兴趣的:(【BZOJ1015】【tyvj3487】星球大战starwar,特别的并查集技巧)