題目描述
#分析
考慮如何才能讓白邊顯得更(不)重要,即在每條白邊上(加上)減去一個值。
我們可以二分這個值,然後用尋常方法做最小生成樹。統計在此最小生成樹裏有多少白 邊。
然後我們就可以找到一個合適的值,帶這個權做一次最小生成樹。
在計算答案的時候把這些值補償回去就做完了。
以上來自某標答
關於各種調試時的槽點
1.二分卡死的情況,什麼l==r然後死循
2.sum必須在外面減去增加的值(這可真是個未解之謎)(爲什麼不可以邊做邊補償?邊做邊補償只有40分。。。)
3.你以爲我寫的是前向星嗎。。。你可看到了head數組?對,就是沒用了。kruskal表示我用不上那東西
關於考試
還有40分鐘
emm先20分鐘來個Kruskal的小板,一看就和最小生成樹有關(題幹)
然後?然後就瓜不瓜?瓜哉瓜哉,瓜了10分鐘
然後又開始了玄學貪心,先把最小的k條白邊加進去騙分
騙了10分
以下正解
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,k,sum,cnt=0,x[1000010],y[1000001],z[1000001],c[1000001],fa[1000001];
struct node{
int u,v,w,c;
}e[2000001];
bool comp(node p,node q){
if(p.w==q.w)return p.c<q.c;
return p.w<q.w;
}
int getfa(int x){
if(fa[x]!=x)
fa[x]=getfa(fa[x]);
return fa[x];
}
void add(int x,int y,int z,int c){
e[++cnt].u=x;e[cnt].v=y;e[cnt].w=z;e[cnt].c=c;
}
bool Kruskal(int mid){
int tot=0,ans=0;
sum=0;
for(int i=0;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
add(x[i],y[i],z[i],c[i]);
if(c[i]==0)
e[cnt].w+=mid;
}
sort(e+1,e+m+1,comp);
for(int i=1;i<=m;++i){
int p=getfa(e[i].u),q=getfa(e[i].v);
if(p!=q){
fa[p]=q;
tot++;
sum+=e[i].w;
if(e[i].c==0)ans++;
}
if(tot>=n-1)
break;
}
cnt=0;
if(ans>=k)return true;
else return false;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
scanf("%d%d%d%d",&x[i],&y[i],&z[i],&c[i]);
int l=-100,r=100;
while(l<r){
int mid=(l+r+1)/2;
if(Kruskal(mid))l=mid;
else r=mid-1;
}
Kruskal(l);
printf("%d",sum-k*l);
return 0;
}