題目
題解
刷水題一直不好意思寫單篇題解,直到我看見了這道題。
把百度搜出來的題解看了前兩頁。全是二分答案,我纔不會告訴你這題,不用二分。當然我一開始也覺得是道二分水題。不過其實不二分也比較水,因爲二分能過所以大家沒有想太多,二分也是經典套路。
我寫到check的時候猶豫了。check在幹啥?把cost <=mid 的一級邊先加進去,然後加剩下的二級邊看能否連通,我怎麼覺得這每次做的動作差不多啊。於是誕生了不用二分的想法,若一級邊爲a邊,二級邊爲b邊,a,b先排序,然後先加k條a邊進圖,注意,這裏不能只加前k小的a邊,因爲有的a邊加不進去,要成功加入k次才行。加完之後,兩個pointer,分別表示a,b邊加到哪裏了,在不連通的時候,每次加代價比較小的邊,直到連通。其實你有沒有發現,先加k條a邊進圖之後,這就是個最小生成樹了,因爲限制條件沒了,妙。
代碼
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
const int maxm=20000+10;
struct Edge{
int u,v,c;Edge(int u=0,int v=0,int c=0):u(u),v(v),c(c){}
inline bool operator < (const Edge &rhs)const{return c<rhs.c;}
}e1[maxm],e2[maxm];
int p[maxn],cnt;
int findset(int x){return p[x]==x?x:p[x]=findset(p[x]);}
inline void merge(int u,int v)
{
u=findset(u);v=findset(v);
if(u!=v) p[u]=v,--cnt;
}
int main()
{
int n,k,m;cin>>n>>k>>m;cnt=n;--m;
if(n==1) return puts("0"),0;
for(int i=1,u,v,c1,c2;i<=m;++i)
{
scanf("%d%d%d%d",&u,&v,&c1,&c2);
e1[i]=Edge(u,v,c1);e2[i]=Edge(u,v,c2);
}
sort(e1+1,e1+m+1);sort(e2+1,e2+m+1);
for(int i=1;i<=n;++i) p[i]=i;
int p1=1,p2=1;
while(cnt != n-k) merge(e1[p1].u,e1[p1].v),++p1;
while(cnt!=1)
{
if(p2==m+1 || e1[p1].c < e2[p2].c) merge(e1[p1].u,e1[p1].v),++p1;
else merge(e2[p2].u,e2[p2].v),++p2;
}
cout<<max(e1[p1-1].c,e2[p2-1].c)<<endl;
return 0;
}