P2323 [HNOI2006]公路修建問題
阿新 • • 發佈:2018-11-01
一道二分好題。
https://www.luogu.org/problemnew/show/P2323
單調性:花費最少的公路要求的費用越低,滿足條件的一級&二級公路就越多。
所以,我們二分花費最少的公路的費用即可。
check函式:看在花費最少的公路的費用的限制下,滿足條件的公路是否能湊出一棵生成樹。我們用類似於kruskal演算法的方法進行這個過程,不過有一個地方需要注意:優先選擇一級公路費用<=當前限制的邊,這樣才更有可能使選出的方案中1級公路的條數>=k。如果先選二級公路,那麼很有可能再選一級公路時,該條公路可能會由於其兩個端點已在當前生成樹的點集中而不被選取,從而導致最終的生成樹中包含了<k條一級公路。
Code:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ri register int using namespace std; const int MAXN=20020; int n,m,k,maxn,l,r,mid,ans; int used[MAXN],had[MAXN],fa[MAXN]; struct node{ int u,v,w1,w2,num; }s[MAXN]; bool cmp(node a,node b) { return a.w1 > b.w1; } int find(int x) { if(x==fa[x]) return x; return fa[x]=find(fa[x]); } bool check(int minn) { int stot=0,cnt=0,fst=1; memset(used,0,sizeof(used)); for(ri i=1;i<=n;i++) fa[i]=i; for(ri i=1;i<=m;i++) if(s[i].w1<=minn) { fst=i; break; } for(ri i=fst;i<=m;i++)//優先挑一級公路費用合法的路 { int r1=find(s[i].u),r2=find(s[i].v); if(r1!=r2) { stot++,used[s[i].num]=1,cnt++; fa[r2]=r1; } } if(cnt<k) return 0; for(ri i=1;i<=fst-1;i++)//挑完一級公路費用合法的路後再挑二級公路 { if(s[i].w2>minn) continue; if(stot==n-1&&cnt>=k) { for(int i=1;i<=m;i++) had[i]=used[i]; return 1; } int r1=find(s[i].u),r2=find(s[i].v); if(r1!=r2) { stot++,used[s[i].num]=2; fa[r2]=r1; } } return 0; } int main() { scanf("%d%d%d",&n,&k,&m); m-=1; for(ri i=1;i<=m;i++) { scanf("%d%d%d%d",&s[i].u,&s[i].v,&s[i].w1,&s[i].w2); maxn=max(maxn,s[i].w1); s[i].num=i; } sort(s+1,s+m+1,cmp); l=0,r=maxn+1; while(l+1<r)//二分最小費用 { mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid; } for(ri i=l;i<=r;i++) if(check(i)) { ans=i; break; } cout<<ans<<'\n'; for(ri i=1;i<=m;i++) if(had[i]) cout<<i<<" "<<had[i]<<'\n'; return 0; }