填坑第$n$日……链接:http://cogs.pro/cogs/problem/problem.php?pid=1764
题意:图中有黑白双色边,要求构造出的生成树必须有恰好$need$条白色边,求出在此条件下最小生成树权值和。保证有解。
为了这道题我冒着查到被杀的风险在学校不午休逃到机房……敲了一中午再加上下午第一节课……做出来之后只能无限$orzWJMZBMR$……
首先可以证明出来,如果给白色边权值全部加上一个定值,新生成的最小生成树中白色边的数量是不增的。我们就利用这个性质,手动给白色边全部加上一个权值,再建树,看看在这个权值下是不是可以抽出$need$条边。由于存在单调性,显然可以二分答案(加粗的字已经说明了一切)。如果这个值合法,那么就将这个情况下答案对应的白边增加的权值再减回去更新答案。
但是这样会带来一个问题:有一些测试点,你加上$mid$数量太少,你加上$mid+1$数量又太多。这种情况下我们就要考虑$Kruskal$的原理,然后我们会发现,这种情况出现前提是:白边与黑边权值重复太多。解决办法很简单也很巧妙:把白边用黑边代替,更新答案时只减去$need$条边造成的影响即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int maxn=50005,maxm=100005; 7 struct node 8 { 9 int from,to,dis,col,next; 10 bool operator <(const node &b)const 11 { 12 return dis<b.dis||(dis==b.dis&&col<b.col); 13 } 14 }edge[maxm]; 15 int head[maxn],tot; 16 void addedge(int u,int v,int w,int x) 17 { 18 edge[++tot]=(node){u,v,w,x,head[u]};head[u]=tot; 19 } 20 int fa[maxn]; 21 int getfa(int x) 22 { 23 return fa[x]==x?x:fa[x]=getfa(fa[x]); 24 } 25 void unionn(int x,int y) 26 { 27 x=getfa(x),y=getfa(y); 28 if(x!=y)fa[y]=x; 29 } 30 int n,m,need; 31 int val; 32 bool check(int add) 33 { 34 val=0; 35 for(int i=0;i<n;i++)fa[i]=i; 36 for(int i=1;i<=m;i++) 37 if(!edge[i].col)edge[i].dis+=add; 38 sort(edge+1,edge+m+1);int num=0,cnt=0; 39 for(int i=1;i<=m;i++) 40 { 41 int u=edge[i].from,v=edge[i].to; 42 if(getfa(u)!=getfa(v)) 43 { 44 unionn(u,v); 45 val+=edge[i].dis; 46 if(!edge[i].col)cnt++; 47 } 48 } 49 for(int i=1;i<=m;i++) 50 if(!edge[i].col)edge[i].dis-=add; 51 return cnt>=need; 52 } 53 int haha() 54 { 55 freopen("nt2012_tree.in","r",stdin); 56 freopen("nt2012_tree.out","w",stdout); 57 scanf("%d%d%d",&n,&m,&need); 58 for(int i=1;i<=m;i++) 59 { 60 int u,v,w,x;scanf("%d%d%d%d",&u,&v,&w,&x); 61 addedge(u,v,w,x); 62 } 63 int l=-105,r=105,mid,query; 64 while(l<=r) 65 { 66 mid=(l+r)>>1; 67 if(check(mid))l=mid+1,query=val-mid*need; 68 else r=mid-1; 69 } 70 printf("%d ",query); 71 } 72 int sb=haha(); 73 int main(){;}