luogu P2700 逐个击破 树dp

传送门

好题啊

给定边权树 求隔离所有指定点的最小花费

考虑树dp的话 自然想到

f[x]表示子树内处理完从根节点出发没有敌人的最小花费

g[x]表示子树内处理完从根节点出发仍有敌人的最小花费 这个时候仍然合法()

又显然根节点是否有敌人是有影响的 所以分类讨论

首先子树没有敌人不用考虑

I. 根节点有敌人的话 f[x]就是inf

g[x]直接取f[v]和g[v]+cst[i]最小值

表示是否切x->v这条边

II. 如果根节点没有

那么g[x]维护的就是选择一个花费最小的儿子切

而这样的花费就是切掉其他所有儿子的花费加上这个的g[v]

所以我们回溯更新f[]和g[]之前先处理出子树是否有敌人和切掉所有儿子的花费

方程看代码吧 强烈建议自己手推

注意这题开longlong!!!(我的30min啊..)

还有就是不要忘了初值还有读入有0啥的

Time cost : 75min

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<queue>
 6 #include<vector>
 7 #define itn int
 8 #define ms(a,b) memset(a,b,sizeof a)
 9 #define rep(i,a,n) for(int i = a;i <= n;i++)
10 #define per(i,n,a) for(int i = n;i >= a;i--)
11 #define inf 1e13
12 using namespace std;
13 typedef long long ll;
14 #define int ll
15 ll read() {
16     ll as = 0,fu = 1;
17     char c = getchar();
18     while(c < ‘0‘ || c > ‘9‘) {
19         if(c == ‘-‘) fu = -1;
20         c = getchar();
21     }
22     while(c >= ‘0‘ && c <= ‘9‘) {
23         as = as * 10 + c - ‘0‘;
24         c = getchar();
25     }
26     return as * fu;
27 }
28 //head
29 const int N = 100003;
30 int head[N],nxt[N<<1],mo[N<<1],cnt;
31 ll cst[N<<1];
32 void _add(int x,int y,ll w) {
33     mo[++cnt] = y;
34     cst[cnt] = w;
35     nxt[cnt] = head[x];
36     head[x] = cnt;
37 }
38 void add(int x,int y,ll w) {if(x^y)_add(x,y,w),_add(y,x,w);}
39
40 ll w[N];
41 ll f[N],g[N];
42
43 bool col[N],vis[N];
44 void dfs(int x,int p) {
45     f[x] = g[x] = 0ll,vis[x] = col[x];
46     ll tot = 0ll;
47     for(int i = head[x];i;i = nxt[i]) {
48         int sn = mo[i];
49         if(sn == p) continue;
50         dfs(sn,x),vis[x] |= vis[sn];
51         tot += min(f[sn],g[sn] + cst[i]);
52     }
53
54     if(col[x]) {
55         f[x] = inf;
56         for(int i = head[x];i;i = nxt[i]) {
57             int sn = mo[i];
58             if(sn == p || !vis[sn]) continue;
59             if(col[sn]) g[x] += g[sn] + cst[i];
60             else g[x] += min(f[sn],g[sn] + cst[i]);
61         }
62
63     } else {
64         g[x] = tot;
65         for(int i = head[x];i;i = nxt[i]) {
66             int sn = mo[i];
67             if(sn == p || !vis[sn]) continue;
68             g[x] = min(g[x], tot - min(f[sn],g[sn] + cst[i]) + g[sn]);
69             if(col[sn]) f[x] += g[sn] + cst[i];
70             else f[x] += min(f[sn],g[sn] + cst[i]);
71         }
72     }
73 }
74
75 signed main() {
76     int n = read(),k = read();
77     rep(i,1,k) col[read()+1] = 1;
78     rep(i,2,n) {
79         int x = read() + 1,y = read() + 1;
80         add(x,y,read());
81     }
82     dfs(1,1);
83     printf("%lld\n",min(f[1],g[1]));
84     return 0;
85 }

To be continued 这题还有生成树的O(nlgn)?

虽然复杂度不优秀但是应该好想啊

再看看

原文地址:https://www.cnblogs.com/yuyanjiaB/p/9902552.html

时间: 2024-07-30 22:30:20

luogu P2700 逐个击破 树dp的相关文章

p2700 逐个击破

题目描述-->p2700 逐个击破 题意概括 花费最小的代价,使得一些有标记的节点不连通. 分析 我们需要花费最小代价使得原来连通的图中一些节点之间不相互连通. 贪心显然是可行的(一点也不显然 看到其他人写了dp,写了贪心. 但我感觉可以排序+并查集做啊. 排序 考虑我们要花费最小代价删边,但是并查集不支持删除操作. (貌似有一种东西叫分治线段树可以维护这种操作. 因此,我们根据容斥原理(这玩意是叫容斥吧. 花费最小代价删边,等价于花最大代价建边,最后剩下不建的边,就是我们的答案. 所以说,我们

洛谷P2700 逐个击破

P2700 逐个击破 题目背景 三大战役的平津战场上,傅作义集团在以北平.天津为中心,东起唐山西至张家口的铁路线上摆起子一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜.为了就地歼敌不让其逃走,毛主席制定了先切断敌人东西两头退路然后再逐个歼灭敌人的战略方针.秉承伟大军事家的战略思想,作为一个有智慧的军长你,遇到了一个类似的战场局面. 题目描述 现在有N个城市,其中K个被敌方军团占领了,N个城市间有N-1条公路相连,破坏其中某条公路的代价是已知的,现在,告诉你K个敌方军团所在的城市,以及所有公路破坏

P2700 逐个击破 最小生成树

题目描述 现在有N个城市,其中K个被敌方军团占领了,N个城市间有N-1条公路相连,破坏其中某条公路的代价是已知的,现在,告诉你K个敌方军团所在的城市,以及所有公路破坏的代价,请你算出花费最少的代价将这K个地方军团互相隔离开,以便第二步逐个击破敌人. 输入输出格式 输入格式: 第一行包含两个正整数n和k. 第二行包含k个整数,表示哪个城市别敌军占领. 接下来n-1行,每行包含三个正整数a,b,c,表示从a城市到b城市有一条公路,以及破坏的代价c.城市的编号从0开始. 输出格式: 输出一行一个整数,

luogu P2607 [ZJOI2008] 骑士 树dp

传送门 又一个没有上司的舞会 这个dp有环 妈妈怎么办啊 要不...环上随便断一条边? 然后最后选的时候分别取两个根节点不选的情况的最大值 几个要点: 1.图可能是多个环套树 要循环走完 2.不能只记录顶点 因为如果有重边的话会把二元环筛掉 3.位运算优先级... 要写成(i^1)==cntline Time cost inf 这题从上周就开始D 一度放弃 今天想整一下以前做过的所有题然后就 就写出来啦!!(开心) Code: (边界写的比较奇怪 是Debug的时候被吓怕了) 1 #includ

luogu P1270 &quot;访问&quot;美术馆 树dp

传送门 比较奇怪的树形背包 首先需要处理读入的问题 这题史诗递归读入 然后递归读入就不用建图 这题特点是只有叶子有价值 所以背包就不太有用 坑点就是这个人进去还得出来... 而且不能把时间都用完(导致75) Time cost: 35min Code: 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<queue> 6

2010辽宁省赛 NBUT 1222 English Game【字典树+DP】

[1222] English Game 时间限制: 1000 ms 内存限制: 131072 K 链接:Click Here! 问题描述 This English game is a simple English words connection game. The rules are as follows: there are N English words in a dictionary, and every word has its own weight v. There is a wei

uva 12452 Plants vs. Zombies HD SP (树DP)

Problem I: Plants vs. Zombies HD Super Pro Plants versus Zombies HD Super Pro is a game played not a grid, but on a connected graph G with no cycles (i.e., a tree). Zombies live on edges of the tree and chew through edges so that tree falls apart! Pl

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu