1. 程式人生 > >[國家集訓隊2]Tree I

[國家集訓隊2]Tree I

一起 www etc ios ble getchar() pan ++ ace

題意

Here

思考

\(WQS\) 二分,第一次做,感覺細節有點多。

由於要求選 \(need\) 條白邊,我們考慮每次給所有白邊加上一個權值,再與黑邊一起做生成樹,這樣就可以限制我們加入白邊的個數了,但是這樣我們還存在一個問題,如果有白邊等於黑邊權值,我們可能會先統計黑邊,造成白邊達不到 \(need\) 條的情況,我們只用在排序時加上第二關鍵字,優先選擇白邊即可。

代碼


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x * f;
}
const int M = 100010;
const int N = 50050;
struct node{
    int from, to, dis, op, now;
}edge[M << 1];
bool cmp(node a, node b){
    return (a.now == b.now) ? a.op < b.op : a.now < b.now;
}
int fa[N];
int n, m, need, ans, sum, js;
int findx(int x){
    if(fa[x] == x) return x;
    return fa[x] = findx(fa[x]);
}
void mergex(int x, int y){
    int fx = findx(x), fy = findx(y);
    if(fx == fy) return;
    fa[fx] = fy;
}
bool check(int del){
    js = 0; sum = 0;
    for(int i=1; i<=n; i++) fa[i] = i;
    for(int i=1; i<=m; i++){
        edge[i].now = edge[i].dis;
        if(edge[i].op == 0) edge[i].now += del;
    }
    sort(edge+1, edge+m+1, cmp);
    for(int i=1; i<=m; i++){
        int u = edge[i].from, v = edge[i].to;
        int fu = findx(u), fv = findx(v);
        if(fu != fv){
            mergex(u, v); sum += edge[i].dis;
            if(edge[i].op == 0) js ++;
        }
    }
    return js >= need;
}
int main(){
    n = read(), m = read(), need = read();
    for(int i=1; i<=m; i++){
        edge[i].from = read(), edge[i].to = read(), edge[i].dis = read(), edge[i].op = read();
        edge[i].from ++, edge[i].to ++;
        if(edge[i].op == 1) edge[i].now = edge[i].dis;
    }
    int l = -100, r = 100;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(check(mid)){
            l = mid + 1;
            ans = sum;
        }
        else r = mid - 1;
    }
    cout<<ans;
    return 0;
}

總結

註意二分的判斷條件是大於等於 \(need\) ,以及排序時註意第二關鍵字以避免無法出解。

[國家集訓隊2]Tree I