BZOJ3060/POI 2012 Tour de Byteotia

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;
}
發佈了39 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章