luogu P2700 逐个击破 树dp
Posted prophetB
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
",min(f[1],g[1]));
84 return 0;
85 }
To be continued 这题还有生成树的O(nlgn)?
虽然复杂度不优秀但是应该好想啊
再看看
以上是关于luogu P2700 逐个击破 树dp的主要内容,如果未能解决你的问题,请参考以下文章