• 洛谷1197并查集+离线+删点+判连通分量


    题目链接:https://www.luogu.com.cn/problem/P1197

    题目给出一些点之间边,然后要摧毁某些结点,问每一次摧毁之后的图的连通分量数量。我们考虑到判连通分量的数量只要扫一遍并查集的根节点就可以,所以考虑用这种数据结构来检查连通分量的数量。其次,并查集中删除结点的消耗是非常大的,每次删除结点需要扫描所有的点才能知道连通分量的数量。但是添加结点的效率是非常高的,所以我们考虑倒过来操作,先把将所有没有摧毁了的点的边连上,然后每次都添加一个最后删除的点,检查一遍连通分量(只要结合一次连通分量必然减少一个),最终把所有的点都连上便是初始状态。

    代码如下:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 typedef unsigned int ui;
      4 typedef long long ll;
      5 typedef unsigned long long ull;
      6 #define pf printf
      7 #define mem(a,b) memset(a,b,sizeof(a))
      8 #define prime1 1e9+7
      9 #define prime2 1e9+9
     10 #define pi 3.14159265
     11 #define lson l,mid,rt<<1
     12 #define rson mid+1,r,rt<<1|1
     13 #define scand(x) scanf("%llf",&x) 
     14 #define f(i,a,b) for(int i=a;i<=b;i++)
     15 #define scan(a) scanf("%d",&a)
     16 #define mp(a,b) make_pair((a),(b))
     17 #define P pair<int,int>
     18 #define dbg(args) cout<<#args<<":"<<args<<endl;
     19 #define inf 0x3f3f3f3f
     20 const int maxn=1e6+10;
     21 int n,m,t;
     22 inline int read(){
     23     int ans=0,w=1;
     24     char ch=getchar();
     25     while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
     26     while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
     27     return ans*w;
     28 }
     29 int f[maxn],vis[maxn],id[maxn],head[maxn],nxt[maxn],num[maxn];
     30 int e;
     31 struct node{
     32     int u,v;
     33 }p[maxn];
     34 void init()
     35 {
     36     f(i,0,n-1)f[i]=i;
     37     mem(vis,0);
     38     e=0;
     39     mem(num,0);
     40     mem(head,-1);
     41     mem(nxt,-1);
     42  } 
     43 int find(int x)
     44 {
     45     if(x==f[x])return x;
     46     return f[x]=find(f[x]);
     47 }
     48 void addedge(int u,int v)
     49 {
     50     p[e].v=v;
     51     p[e].u=u;
     52     nxt[e]=head[u];
     53     head[u]=e++;
     54 }
     55 int main()
     56 {
     57     //freopen("input.txt","r",stdin);
     58     //freopen("output.txt","w",stdout);
     59     std::ios::sync_with_stdio(false);
     60     n=read(),m=read();
     61     init();
     62     int u,v;
     63     f(i,1,m)
     64     {
     65         u=read(),v=read();
     66         addedge(u,v);//建边不用管权值,只需要知道两个点是否相连,并且并查集中的边是双向连通的
     67         addedge(v,u); 
     68     }
     69     t=read();
     70     f(i,1,t)id[i]=read(),vis[id[i]]=1;
     71     int cnt=n-t;//最初有n-k个点,也就是n-k个连通分量,是k次打击之后剩下来的点 
     72     f(i,0,n-1)
     73     {
     74         for(int j=head[i];~j;j=nxt[j])
     75         {
     76             if(!vis[p[j].u]&&!vis[p[j].v])//枚举每个点的每条边,开始建立摧毁完结点之后的图 
     77             {
     78                 int x=find(p[j].u);
     79                 int y=find(p[j].v);
     80                 if(x==y)continue;
     81                 else cnt--,f[x]=y;//每次合并两个连通分量之后分量数量都减一 
     82             }
     83         }
     84     }
     85     num[t]=cnt;
     86     for(int i=t;i>=1;i--)
     87     {
     88         int s=id[i];
     89         cnt++;//修复一个点之后连通分量加一
     90         vis[s]=0;//加点之后注意标记这个点是未被打击的,图中只保持未被打击的点之间有连线 
     91         for(int j=head[s];~j;j=nxt[j])
     92         {
     93             if(!vis[p[j].v])
     94             {
     95                 int x=find(p[j].u);
     96                 int y=find(p[j].v);
     97                 if(x==y)continue;
     98                 else cnt--,f[x]=y;    
     99             }
    100         }
    101         num[i-1]=cnt;
    102     }
    103     f(i,0,t)pf("%d
    ",num[i]);
    104  } 
  • 相关阅读:
    第025讲:字典:当索引不好用时 | 课后测试题及答案
    第023、024讲:递归:这帮小兔崽子、汉诺塔 | 课后测试题及答案
    第022讲:函数:递归是神马
    第021讲:函数:lambda表达式
    第020讲:函数:内嵌函数和闭包
    第019讲:函数:我的地盘听我的
    第018讲:函数:灵活即强大
    第017讲:函数
    第016讲:序列!序列
    第015讲:字符串:格式化
  • 原文地址:https://www.cnblogs.com/randy-lo/p/12566607.html
Copyright © 2020-2023  润新知