带逆向思维的并查集

有一类并查集题目中,起始是一张连了所有边的图。

给出一些操作,让一些边"断裂",再抛出一些问题。

这个时候,就可以考虑逆向合并边。


例题星球大战:https://www.luogu.com.cn/problem/P1197

 

思路:

  因为每次轰炸星球,相当于把一个点完全抹除,相应的边也没了。

  那么我们完全可以先读入要摧毁的点,标记起来,然后在剩余的边里面

  挑出一些没有被摧毁的先建边。很神奇的,我们发现这个时候的连通块个数就是最后一次的个数

  那我们再把最后一个被摧毁的点相应的边连起来,再看看连通块个数,这个答案就是倒数第二次的答案

不过要清楚一点。图中7和1起始也有边,但是我们从后逆推,那么1,3,5是已经被摧毁了的,所以我们暂时不能连边

还有一点,每次我们连完了一个点对应的边,就要把它的标记清楚。因为再往前逆推,那么这个点其实是没有被摧毁的

然后每次连接完一个点后,当前连通块个数都要+1

 

 

#include <bits/stdc++.h>
using namespace std;
const int maxn=400009;
struct node{
    int to,nxt;
}d[maxn*2];
int head[maxn*2],cnt=1,pre[maxn],n,m,q;
void add(int u,int v){
    d[cnt].to=v,d[cnt].nxt=head[u],head[u]=cnt++;
}
int find(int x){
    if(x!=pre[x])    pre[x]=find(pre[x]);
    return pre[x];
}
void join(int q,int w){
    pre[find(q)]=find(w);
}
int vis[maxn],a[maxn],ans[maxn],ww;
void init()
{
    for(int i=1;i<=n;i++)
    {
        if(vis[i])    continue;//被摧毁的星球不连边
        for(int j=head[i];j;j=d[j].nxt)
        {
            if(vis[d[j].to])    continue;
            if(find(d[j].to)!=find(i))
                ww--,join(d[j].to,i);    
        } 
    }
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<=n-1;i++)    pre[i]=i;
    for(int i=1;i<=m;i++)
    {
        int l,r;
        cin>>l>>r;
        add(l,r);add(r,l);
    }
    cin>>q;
    ww=n-q;
    for(int i=1;i<=q;i++)
    {
        int x;cin>>x;
        vis[x]=1;a[i]=x;
    }
    init();//连初始边 
    for(int i=q;i>=1;i--)
    {
        ans[i]=ww;
        for(int k=head[a[i]];k;k=d[k].nxt)
        {
            int z=d[k].to;
            if(vis[z])    continue;
            if(find(z)!=find(a[i]))
                ww--,join(z,a[i]);
        }
        vis[a[i]]=0;
        ww++;//连通块个数加一 
    }
    cout<<ww<<endl;
    for(int i=1;i<=q;i++)    cout<<ans[i]<<endl;
} 

 

posted @ 2020-03-12 13:50  倾叶子佮  阅读(223)  评论(0编辑  收藏  举报