LuoguP2700逐个击破并查集/生成树/正难则反By cellur925
Posted cellur925&Chemist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LuoguP2700逐个击破并查集/生成树/正难则反By cellur925相关的知识,希望对你有一定的参考价值。
题目大意:给你一棵树,求把其中k个点相互隔离(不连通)所需要的边权代价。
这题我开始是想要求出把k个点联通的最小代价的,但后来发现还是实现起来比较困难,题解里貌似也没有这种做法,于是就鸽了。但是大体的思考方向还是不直接去想把k个点隔离,而是把问题转化。
花费最小代价删边->花费最大代价建边。而建边的时候如果遇到一条两边都是敌人的边,我们显然是不需要建的,所以这其实我们需要维护敌人的网络,用并查集来维护。
首先我们标记敌人点,再把边从大到小排序。枚举所有的边,如果它两端点都是敌人,那肯定不连他。如果两端点有一个是敌人,也连上,并在那个无辜的点上打一个标记,因为之后的点也不能再连他。于是我们就可以用并查集维护。最后注意我们之后调用的都是这个集合的代表元,即getf。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #define maxn 100090 4 5 using namespace std; 6 typedef long long ll; 7 8 int n,k; 9 ll ans; 10 int vis[maxn],fa[maxn]; 11 struct node{ 12 int to,from,val; 13 }edge[maxn*2]; 14 15 bool cmp(node a,node b) 16 { 17 return a.val>b.val; 18 } 19 20 int getf(int x) 21 { 22 if(fa[x]==x) return x; 23 return getf(fa[x]); 24 } 25 26 int main() 27 { 28 scanf("%d%d",&n,&k); 29 for(int i=1;i<=k;i++) 30 { 31 int x=0; 32 scanf("%d",&x); 33 vis[x]=1; 34 } 35 for(int i=1;i<=n-1;i++) 36 scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].val),ans+=edge[i].val; 37 for(int i=1;i<=n;i++) fa[i]=i; 38 sort(edge+1,edge+1+n-1,cmp); 39 for(int i=1;i<=n-1;i++) 40 { 41 int pp=getf(edge[i].from),qq=getf(edge[i].to); 42 if(vis[pp]&&vis[qq]) continue; 43 ans-=edge[i].val; 44 if(qq!=pp) fa[qq]=pp; 45 if(vis[pp]) vis[qq]=1; 46 else if(vis[qq]) vis[pp]=1; 47 } 48 printf("%lld",ans); 49 return 0; 50 }
以上是关于LuoguP2700逐个击破并查集/生成树/正难则反By cellur925的主要内容,如果未能解决你的问题,请参考以下文章