LuoguP2700逐个击破【并查集/生成树/正难则反】By cellur925
2018-10-05 07:31
267 查看
题目大意:给你一棵树,求把其中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
相关文章推荐
- 并查集【p2700】逐个击破
- 洛谷P2700 逐个击破
- 洛谷 2700 逐个击破
- Luogu P1195/P1892 口袋的天空/BOI团伙 【最小生成树/并查集】By cellur925
- 【luogu2700】逐个击破
- luogu 2700 逐个击破
- [来源未知][树形dp]逐个击破
- 最小生成树 - 克鲁斯卡尔 - 并查集 - 边稀疏 - O(E * logE)
- 最小生成树Kruskal算法与并查集及其优化
- poj 2421 Constructing Roads 并查集+最小生成树
- 图算法 最小生成树邻接表 Kruskal算法(并查集)
- C#难点逐个击破3params数组参数
- [luoguP1783] 海滩防御(二分 || 最短路 || 最小生成树)
- POJ 1233 并查集,最小生成树
- NYOJ 38布线问题(并查集)(最小生成树Kruskal)
- 最小生成树总结(prim、并查集和kruskal) C++实现
- C#难点逐个击破(6):C#数据类型与.net framework数据类型
- HDU 1233 还是畅通工程 最小生成树Kruskal算法/并查集
- 最小生成树kruskal算法(并查集)