1. 程式人生 > >BZOJ2654 & 洛谷2619:tree——題解

BZOJ2654 & 洛谷2619:tree——題解

cnblogs uri type 最好 -s dig you -m span

https://www.lydsy.com/JudgeOnline/problem.php?id=2654

https://www.luogu.org/problemnew/show/P2619

給你一個無向帶權連通圖,每條邊是黑色或白色。讓你求一棵最小權的恰好有need條白色邊的生成樹。 題目保證有解。

在APIO重學了二分……cljls的可怕題目!

如果不知道wqs二分(DP凸優化/帶權二分?)的話應該是沒什麽思路的。

對每條白邊加整數w,如果w過小則求得的生成樹大部分為白邊,反之大部分為黑邊。

於是利用這個性質二分w,且正好當符合條件時求得的生成樹就正是題目所求得生成樹。

為什麽呢?顯然我們拿的白邊是所有白邊當中最好的(權值最小的),且由於kruskal的正確性不難知道其正確性。

PS:當邊權相等時優先添加白邊!

#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const
int N=50005; const int M=100005; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==-;ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int u,v,w,c; }e[M]; int n,m,t,tmp,fa[N]; int find(int x){
if(fa[x]==x)return x; return fa[x]=find(fa[x]); } inline void unionn(int u,int v){ fa[u]=v; } inline bool cmp(node a,node b){ return a.w<b.w||(a.w==b.w&&a.c<b.c); } void modify(int k){ for(int i=1;i<=m;i++){ if(!e[i].c)e[i].w+=k; } } bool pan(int k){ modify(k); sort(e+1,e+m+1,cmp); int num=0,cnt=0;tmp=0; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++){ int u=e[i].u,v=e[i].v,w=e[i].w; u=find(u),v=find(v); if(u!=v){ unionn(u,v); cnt++;tmp+=w; if(!e[i].c)num++; if(cnt==n-1)break; } } modify(-k); return num>=t; } int solve(int l,int r){ int ans; while(l<r){ int mid=(l+r+1)>>1; if(!pan(mid))r=mid-1; else{ l=mid; ans=tmp-mid*t; } } return ans; } int main(){ n=read(),m=read(),t=read(); for(int i=1;i<=m;i++){ e[i].u=read()+1,e[i].v=read()+1; e[i].w=read(),e[i].c=read(); } printf("%d\n",solve(-100,100)); return 0; }

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+歡迎訪問我的博客:http://www.cnblogs.com/luyouqi233/ +

+++++++++++++++++++++++++++++++++++++++++++

BZOJ2654 & 洛谷2619:tree——題解