P2619 [国家集训队2]Tree I
题解:
今天考到了这道题,一开始看到最小生成树,但越做发现越不简单,想着想着又想到九霄云外去了,竟然先跑一遍mst,用树剖维护最小生成树...但其实c的范围已经在提醒我们可以用二分答案。其实想一想还是很简单的,就是二分所有白边可能减少的边权值,范围在[-1000-1000]之间,(这题还要小一点[-100-100])然后每一次做一下kruskal即可。判断是否满足need,一直二分答案即可。
注意此题的坑点,开始按边权排序的时候,要注意:如果边权相同,要优先选白边。
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+7; struct node{ int x,y,val,col; }tree[maxn*4]; int n,m,need; int tot,cnt,sum,ans; int fa[maxn]; int get(int x){ if(x==fa[x]) return x; else return fa[x]=get(fa[x]); } bool cmp(node a,node b){ if(a.val==b.val) return a.col<b.col; else return a.val<b.val; } int main(){ freopen("e.in","r",stdin); freopen("e.out","w",stdout); scanf("%d%d%d",&n,&m,&need); for(int i=1;i<=m;i++) scanf("%d%d%d%d",&tree[i].x,&tree[i].y,&tree[i].val,&tree[i].col); int l=-1005,r=1005; while(l<r){ int mid=l+(r-l)/2; cnt=tot=sum=0; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) if(tree[i].col==0) tree[i].val+=mid; sort(tree+1,tree+1+m,cmp); for(int i=1;i<=m;i++){ int f1=get(tree[i].x); int f2=get(tree[i].y); if(f1!=f2){ fa[f1]=f2; sum+=tree[i].val; tot++; if(tree[i].col==0) cnt++; if(tot==n-1) break; } } if(cnt>=need){ l=mid+1; ans=sum-need*mid; } else r=mid; for(int i=1;i<=m;i++) if(tree[i].col==0) tree[i].val-=mid; } printf("%d\n",ans); return 0; }