一开始打了个贪心,求最小生成树,白边多就把权值最大的白边干掉,白边少就把权值最大的黑边干掉,因为数据肯定有解嘛。结果大点TLE,小点只过了一个。事实证明,贪心有时真的不能瞎用。
其实这个二分还是挺有道理的,给白边加权值,则入选白边会减少,给白边减权值,则入选白边增加,二分枚举这个权值,白边多了则向大枚举,白边少了则向小枚举,思路蛮简单的。
剩下的就是二分的细节了。试了无数遍,终于找到了最合适的二分方法,而且等号最好放在大于号那里,因为恰好有need条白边以后ans还不一定最优,应该尝试着在白边不变的基础上,把更优秀的黑边加进来,需要把白边排斥一下。
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<stack> #include<set> #include<map> using namespace std; struct EDGE{ int st,ed,nex,val,col; }edge[2005000];int num,first[500050]; int read(){ int sum=0,f=1;char x=getchar(); while(x<'0'||x>'9'){ if(x=='-') f=-1; x=getchar(); }while(x>='0'&&x<='9'){ sum=sum*10+x-'0'; x=getchar(); }return sum*f; } int n,m,need,ans,ans2,fa[500500]; void add(int st,int ed,int val,int col){ edge[++num].st=st; edge[num].ed=ed; edge[num].val=val; edge[num].col=col; edge[num].nex=first[st]; first[st]=num; } bool camp(EDGE a,EDGE b){ if(a.val==b.val) return a.col>b.col; return a.val<b.val; } int get(int x){ return x==fa[x]?x:fa[x]=get(fa[x]); } bool check(int x){ // cout<<"mid="<<x<<endl; int color=0,sum=0; ans=0; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=num;i++) if(edge[i].col) edge[i].val+=x; sort(edge+1,edge+1+num,camp); for(int i=1;i<=num;i++){ int x=edge[i].st,y=edge[i].ed; x=get(x);y=get(y); if(x==y) continue; fa[x]=y; color+=edge[i].col; ans+=edge[i].val; sum++; if(sum==n-1) break; } for(int i=1;i<=num;i++) if(edge[i].col) edge[i].val-=x; /* cout<<color<<" "<<endl; for(int i=1;i<=num;i++){ cout<<edge[i].val<<" "; }cout<<endl;*/ // fgetc(stdin); return color>=need; } int main(){ n=read();m=read();need=read(); for(int i=1;i<=m;i++){ int st=read(),ed=read(),val=read(),col=read(); add(st+1,ed+1,val,col^1); } int l=-102,r=102; while(l<r){ // cout<<l<<" "<<r<<endl; int mid=l+r>>1; if(check(mid)){ l=mid+1; ans2=ans-need*mid; } else r=mid; }printf("%d",ans2); return 0; }