码迷,mamicode.com
首页 > 其他好文 > 详细

luogu P2700 逐个击破 树dp

时间:2018-11-04 00:38:29      阅读:139      评论:0      收藏:0      [点我收藏+]

标签:最小值   sign   def   open   16px   display   http   alt   char   

传送门

好题啊

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

考虑树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 }
View Code

 

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

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

再看看

luogu P2700 逐个击破 树dp

标签:最小值   sign   def   open   16px   display   http   alt   char   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!