【20180807模擬測試】tree

題目描述

或許會傳送失敗的傳送門

#分析

考慮如何才能讓白邊顯得更(不)重要,即在每條白邊上(加上)減去一個值。
我們可以二分這個值,然後用尋常方法做最小生成樹。統計在此最小生成樹裏有多少白 邊。
然後我們就可以找到一個合適的值,帶這個權做一次最小生成樹。
在計算答案的時候把這些值補償回去就做完了。

以上來自某標答

關於各種調試時的槽點
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章