[國家集訓隊2]Tree I
阿新 • • 發佈:2018-11-05
一起 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