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 }
View Code

 

以上是关于LuoguP2700逐个击破并查集/生成树/正难则反By cellur925的主要内容,如果未能解决你的问题,请参考以下文章

树上统计treecnt(dsu on tree 并查集 正难则反)

P2700 逐个击破

luoguP2700 逐个击破

P2700 逐个击破 最小生成树

逐个击破(并查集)

jzoj 2936_逐个击破_并查集