洛谷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\n",num[i]); 104 }
每一个不曾起舞的日子,都是对生命的辜负。