javaee论坛

普通会员

225648

帖子

355

回复

369

积分

楼主
发表于 2019-11-07 13:03:08 | 查看: 82 | 回复: 2

文章目录题目题目大意分析代码

题目

st-SpanningTree

题目大意

给出一个nnn(2≤n≤200 0002\leqn\leq200\0002≤n≤200 000)个点mmm(1≤m≤400 0001\leqm\leq400\0001≤m≤400 000)条边的图(连通,无重边,无自环)和s,t,ds,dts,t,d_s,d_ts,t,ds​,dt​。输出该图的一个生成树,使得sss点的度数不超过dsd_sds​,ttt点度数不超过dtd_tdt​。

分析

先把sss点ttt点扯掉,使图变成几个连通块。由于两棵树之间任意连一条边就变成了一棵树,所以我们可以先把这几个连通块分别做生成树,然后再通过sss和ttt使它们变成一棵树。

把缩过的点分成下面三种情况:

只和sss相连(下图中的AAA)只和ttt相连(下图中的BBB)和sss,ttt都相连(下图中的CCC)

当然,如果有点,既不和sss也不和ttt相连,就无解。显然A−sA-sA−s、B−tB-tB−t的边全部要选,否则图将不连通。接下来只需要考虑CCC类的点。那么只需要让某个点CxC_xCx​把两边的边(Cx−sC_x-sCx​−s,Cx−tC_x-tCx​−t)都保留,剩下的点只留一边的边即可。

具体来说,先要判断现在的dxd_xdx​和dyd_ydy​是否满足dx+dy−1≥cntd_x+d_y-1\geq\text{cnt}dx​+dy​−1≥cnt(cnt\text{cnt}cnt是CCC类点的个数)(减一是有两个边连在同一个点CxC_xCx​上,大于等于是因为度数不超过dxd_xdx​、dtd_tdt​都行),不满足就不可能。然后,你可以直接把ttt的度数用完,因为sss的度数完全可以是111。当然,如果这个时候dxd_xdx​或者dtd_tdt​为000,就不可能了。

还有一种情况,sss与ttt之间有一条边且这条边是割边,那么必须加入边s−ts-ts−t。但是你不用写Tarjan来判断,只需要看最后的图连不连通(因为原图保证了连通,所以这时候肯定有解)(即,选出的边数是否等于n−1n-1n−1),不连通就把s−ts-ts−t放进去就行了。

代码

没有用并查集,有点慢。

#include<bits/stdc++.h>usingnamespacestd;intread(){intx=0;boolf=0;charc=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=x*10+(c^48),c=getchar();returnf?-x:x;}#defineMAXN200000#defineNOreturnputs("No"),0vector<int>G[MAXN+5];vector<pair<int,int>>Ans;intBlock[MAXN+5],NodeS[MAXN+5],NodeT[MAXN+5];voiddfs(intu,intid){Block[u]=id;for(intv:G[u])if(!Block[v]){Ans.push_back({u,v});dfs(v,id);}}intmain(){intN=read(),M=read();for(inti=1;i<=M;i++){intu=read(),v=read();G[u].push_back(v);G[v].push_back(u);}intS=read(),T=read(),DS=read(),DT=read();intid=0;Block[S]=-1,Block[T]=-2;//把s,t拆出来for(inti=1;i<=N;i++)if(!Block[i])dfs(i,++id);//对每个连通块生成树set<int>toS,toT,OneS,OneT,Two;for(intv:G[S])if(v!=T)toS.insert(Block[v]),NodeS[Block[v]]=v;for(intv:G[T])if(v!=S)toT.insert(Block[v]),NodeT[Block[v]]=v;//缩点连边for(inti=1;i<=id;i++){if(toS.count(i)&&toT.count(i))Two.insert(i);elseif(toS.count(i))OneS.insert(i);elseif(toT.count(i))OneT.insert(i);elseNO;}//上面说的三种情况if(OneS.size()>DS||OneT.size()>DT)NO;for(inti:OneS)Ans.push_back({S,NodeS[i]});for(inti:OneT)Ans.push_back({T,NodeT[i]});DS-=OneS.size(),DT-=OneT.size();//只有一边的全部连if(!DS||!DT||DS+DT-1<Two.size())NO;DT=min(DT,int(Two.size()));//t尽量全部连for(inti:Two){if(DT>0)Ans.push_back({T,NodeT[i]}),DT--;if(DT==0)Ans.push_back({S,NodeS[i]});//s与t有一个重合的}if(Ans.size()<N-1)Ans.push_back({S,T});//看加不加s-t的边puts("Yes");for(autoi:Ans)printf("%d%d\n",i.first,i.second);}

普通会员

2

帖子

339

回复

349

积分
沙发
发表于 2023-11-12 10:13:41

标记一下

普通会员

0

帖子

304

回复

318

积分
板凳
发表于 2024-05-09 04:21:28

不错

您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017