【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;
}
posted @ 2018-08-07 15:12  lisuier  阅读(164)  评论(0编辑  收藏  举报