zoukankan      html  css  js  c++  java
  • P1197 [JSOI2008]星球大战

    算法

    并查集+逆序

    思路

    做这道题前呢,我们先出门左转关闭农场,一道类似的更简单一丢丢的题

    然后,我们考虑一下这题,因为并没有过多的操作,只是要我们求一下连通块的个数而已(也就是连通性,具有传递性的连通),而这恰好是并查集所擅长的。

    然而,我们正向看题目时就会发现不支持删除操作的并查集似乎办不到。但,如果我们把删除操作换成加入操作,就可以办到了。这也就是逆向思维。题目支持离线,所以我们可以逆向处理出所有答案以后再全部输出就好了。

    考虑最后的连通状态:标记摧毁的点,把不摧毁的点之间能连的边连起来,同时统计连通块的数量即为所求

    中间的连通状态:于最后的连通状态处理方法类似,从后往前依次加入被删除的点,枚举出边,连通。统计连通块的数量即为所求

    倒序存,正序输shu 

    #include<bits/stdc++.h>
    using namespace std;
    int now[400010],tot;
    int team[400010];
    bool flag[400010];//是否存在
    int father[400010];
    int n,m,k;
    int ans[400010];
    int sum;
    struct node
    {
        int from;
        int to;
        int next;
    }a[400010];//邻接表存图
    void put(int x,int y)
    {
        a[tot].from=x;
        a[tot].next=now[x];
        a[tot].to=y;
        now[x]=tot;
        tot++;
    }
    int find(int x)
    {
        if(x!=father[x])father[x]=find(father[x]);
        return father[x];
    }//并查集基本操作
    void unionn(int x,int y)
    {
        x=find(x),y=find(y);
        if(x==y)return;
        sum--;//合并集合减一
        father[x]=y;
    }//同上
    int main()
    {
        scanf("%d%d",&n,&m);
        int x,y;
        for(int i=0;i<n;i++)
            father[i]=i,now[i]=-1;//初始化
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            put(x,y);
            put(y,x);//存图
        }
        scanf("%d",&k);
       
        sum=n-k;//假设剩下的都不联通
        for(int i=1;i<=k;i++)
        {
            scanf("%d",&x);
            flag[x]=true;
            team[i]=x;
        }
       
        for(int i=0;i<2*m;i++)
        {
            if(!flag[a[i].from]&&!flag[a[i].to])
            {
                unionn(a[i].from,a[i].to);//链接所有边
            }
        }
        //printf("k:%d
    ",k);
        ans[k+1]=sum;
        for(int i=k;i>=1;i--)
        {
            int u=team[i];
            sum++;//新增一个点+1连通块
            flag[u]=false;
            for(int i=now[u];i!=-1;i=a[i].next)
            {
                if(!flag[a[i].from]&&!flag[a[i].to])//都在并查集内(没被摧毁)
                {
                    unionn(a[i].from,a[i].to);
                }
            }
            ans[i]=sum;
        }//倒着做
      // printf("k:%d
    ",k);
        for(int i=1;i<=k+1;i++)
        printf("%d
    ",ans[i]);
    
    }
    

      

  • 相关阅读:
    303. Range Sum Query
    302. Smallest Rectangle Enclosing Black Pixels
    301. Remove Invalid Parentheses
    The Swift.org Blog Welcome
    About Swift
    Swift is Open Source
    Swift is Now Open Source
    加快Terminal的打开加载速度
    加快Terminal的打开加载速度
    [note]What I’ve learnt from working on startups
  • 原文地址:https://www.cnblogs.com/ruanmowen/p/12731110.html
Copyright © 2011-2022 走看看