Task
給定一個n個點m條邊的無向圖,問最少刪掉多少條邊能使得編號小於等於k的點都不在環上。
1 ≤ n ≤ 1,000,000,1 ≤ m ≤ 2,000,000,1 ≤ k ≤ n。
Solution
對於刪邊不容易解決的情況,可以”正難則反”,考慮是否能求出最多能加的邊.對於兩個節點都在[k+1,n]範圍內的邊(設爲1類邊),直接加到圖中(過程1).全部加完之後,把一個聯通塊看做一個點,再往圖中加剩餘的邊.若所加的邊的兩個端點已經連通,那就不加(記爲過程2),與kruskal算法的過程一致.對於過程1,可以保證一定是最優的:
當加完1類邊後,把每個聯通塊看做一個點,點的個數爲x,答案最大爲x+k-1.相當於一棵包含所有點的樹.
假設有邊(u,v)(v>k,u>k):
若u,v已經連通,那麼加入這條邊對圖的構成並沒有影響,而且可以使答案+1.
若u,v不連通,這條邊可以將u,v所在的兩個聯通塊聯通,相當於最後的樹中少了一個節點.要注意,在最後建樹的過程中有可能不構建成一棵樹,也就是說,少了一個節點會使過程2建的邊數少1或不變,而加了(u,v)邊後,答案至少+1,因此這是最優的.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
#include<queue>
#include<set>
using namespace std;
inline void rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>=48);
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
const int M=1e6+5;
int u[M<<1],v[M<<1],fa[M],n,m,K;
int get(int c){
if(fa[c]!=c)fa[c]=get(fa[c]);
return fa[c];
}
int main(){
int n,i,j,tot=0,cas,a,b,k,ans=0;
rd(n);rd(m);rd(K);
for(i=1;i<=n;i++)fa[i]=i;
for(i=1;i<=m;i++){
rd(a);rd(b);
if(a<=K||b<=K){
u[tot]=a;v[tot++]=b;
continue;
}
ans++;
int x=get(a),y=get(b);
if(x!=y)fa[x]=y;
}
for(i=0;i<tot;i++){
int x=get(u[i]),y=get(v[i]);
if(x!=y){
fa[x]=y;
ans++;
}
}
printf("%d\n",m-ans);
return 0;
}