【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;
}