标签:return hellip href 最小 +++ 个性 namespace odi print
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/ +
+++++++++++++++++++++++++++++++++++++++++++
标签:return hellip href 最小 +++ 个性 namespace odi print
原文地址:https://www.cnblogs.com/luyouqi233/p/9026826.html