【题目描述】
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。问收益最大值是多少。
【输入格式】
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。输入保证所有点之间是联通的。
【输出格式】
输出一个正整数,表示收益的最大值。
【输入样例1】
3 1
1 2 1
1 3 2
【输出样例1】
3
【输入样例2】
5 2
1 2 3
1 5 1
2 3 1
2 4 2
【输出样例2】
17
【样例解释】
在第二个样例中,将点1,2染黑就能获得最大收益。
【数据范围】
对于30%的数据,N<=20
对于50%的数据,N<=100
对于100%的数据,N<=2000,0<=K<=N。

题解

很显然是个树dp,然后问题来了,怎么设计状态,根据经验我们可以设计出$dp[i][j]$表示以$i$为根的子树中染了$j$个黑点的贡献,但是这里所说的贡献是对整体还是只考虑局部呢,答案是对整体的贡献,因为很显然这个题目中满足局部最优并不一定就满足整体最优,这样状态定义就结束了,我们在来考虑转移,我们设当前枚举的根节点为$x$,我们再来枚举他的子节点$y$,发现无法直接通过子节点转移到父节点,我们再考虑,是什么连接子节点和父节点,是边,那我们就可以通过边的贡献来转移,那么每条边的贡献就是边权×子树中黑点数×子树外黑点数+边权×子树中白点数×子树外白点数。这样就可以愉快的转移了

$dp[x][j+m]=\max{(dp[y][m]+dp[x][j]+val)}$

其中j枚举x子树大小,m枚举y子树大小,val就是边的贡献。

然后就是要倒着枚举(其实正着枚举也行,就是麻烦),因为如果单纯的正着枚举,会导致用更新过的来更新,而不是正常的转移,即你要用之前的子树大小和当前子树来更新父节点,但如果不做任何处理的正着枚举就会导致用已经更新到size的值来更新,就会不对。

还有就是转移要放到dfs循环里边,其实和上面一样,就是你只是用前面的size,而不是总的size,具体看代码里的注释吧

还有要注意的一点就是因为这题你把所有黑点和白点互换不会对答案产生影响,所以$k=\min{(k,n-k)}$。

一遍dfs即可。

这样做的复杂度时$O(n^2)$的,简单点说就是因为每个点最多只会和其他点乘一次。

 #include<cstdio>
using namespace std;
const int N=,M=,L=<<|;
//#define int long long
#define rg register int
int first[N],nex[M],to[M],edge[M],v[N],size[N],tot,n,k;long long dp[N][N];
char buf[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buf)+fread(buf,1,L,stdin),S==T))?EOF:*S++)
inline int read(){
rg ss=;register char bb=getchar();
while(bb<||bb>)bb=getchar();
while(bb>=&&bb<=)ss=(ss<<)+(ss<<)+(bb^),bb=getchar();
return ss;
}
inline void add(rg a,rg b,rg c){
to[++tot]=b,edge[tot]=c,nex[tot]=first[a],first[a]=tot;
}
inline int max(rg a,rg b){return a>b?a:b;}
inline int min(rg a,rg b){return a<b?a:b;}
inline long long Max(long long a,long long b){return a<b?b:a;}
void dfs(int x){
int y;
size[x]=;v[x]=;
for(int i=first[x];i;i=nex[i]){
if(v[y=to[i]]) continue;
dfs(y);
rg z=edge[i];
rg qq=min(size[x],k),pp=min(size[y],k);
//倒序枚举
for(rg j=qq;j>=;--j)//这块要放到里面,原因见blog
for(rg l=pp;l>=;--l){
long long del=1ll*z*1ll*l*1ll*(k-l)+1ll*z*1ll*(size[y]-l)*1ll*(n-k-(size[y]-l));
dp[x][j+l]=Max(dp[x][j+l],dp[y][l]+dp[x][j]+del);
}
size[x]+=size[y];
}
}
signed main(){
n=read(),k=read();
k=min(k,n-k);
for(rg i=;i<n;++i){
rg x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
dfs();
printf("%lld",dp[][k]);
}

[HAOI2015][bzoj 4033]树上染色(树dp+复杂度分析)的更多相关文章

  1. [BZOJ 4033] 树上染色

    Link: BZOJ 4033 传送门 Solution: 此题用到了计算贡献的方法, 将 多条路径的路径和  $->$ $\sum_{i=1}^{n-1} w[i]*cnt[i]$ 这样我们由 ...

  2. bzoj 4033 树上染色 - 树形动态规划

    有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑 色,并将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的 ...

  3. 洛谷 P3177 [HAOI2015]树上染色 树形DP

    洛谷 P3177 [HAOI2015]树上染色 树形DP 题目描述 有一棵点数为 \(n\) 的树,树边有边权.给你一个在 \(0 \sim n\)之内的正整数 \(k\) ,你要在这棵树中选择 \( ...

  4. bzoj 4033: [HAOI2015]树上染色 [树形DP]

    4033: [HAOI2015]树上染色 我写的可是\(O(n^2)\)的树形背包! 注意j倒着枚举,而k要正着枚举,因为k可能从0开始,会使用自己更新一次 #include <iostream ...

  5. [HAOI2015]树上染色 树状背包 dp

    #4033. [HAOI2015]树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白 ...

  6. [BZOJ4033][HAOI2015]树上染色(树形DP)

    4033: [HAOI2015]树上染色 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2437  Solved: 1034[Submit][Stat ...

  7. 【BZOJ4033】[HAOI2015]树上染色 树形DP

    [BZOJ4033][HAOI2015]树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染 ...

  8. 【HAOI2015】树上染色—树形dp

    [HAOI2015]树上染色 [题目描述]有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色.将所有点染色后,你会获得 ...

  9. bzoj 3572世界树 虚树+dp

    题目大意: 给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制 求每个关键点控制多少个点 分析: 虚树+dp dp过程如下: 第一次dp,递归求出每个点子树中 ...

随机推荐

  1. HuTool之判断上传文件的文件类型

    文件类型判断-FileTypeUtil 由来 在文件上传时,有时候我们需要判断文件类型.但是又不能简单的通过扩展名来判断(防止恶意脚本等通过上传到服务器上),于是我们需要在服务端通过读取文件的首部几个 ...

  2. NIO(3)--Selector

    Selector是NIO中的可选择Channel(SelectableChannel)的multiplexor.有两个拗口的概念,首先是SelectableChannel,在NIO里并非所有的Chan ...

  3. [C#] 匿名方法的方便和安全

    static void Main(string[] args) { int count = 5; //不安全写法 Task.Run(() => { Thread.Sleep(1000); Con ...

  4. PreparedStatement和批处理

    1.概述 PreparedStatement 接口继承了 Statement,并与之在两方面有所不同,它表示预编译的 SQL 语句对象. 首先,数据库会对预编译语句提供性能优化.因为预编译语句有可能被 ...

  5. python逻辑编程之kanren

    https://github.com/logpy/logpy https://pypi.org/project/kanren/ https://www.yiibai.com/ai_with_pytho ...

  6. SocketException: Write failed (OS Error: Broken pipe, errno = 32

    https://github.com/flutter/flutter/issues/16491

  7. Vue通过WebSocket建立长连接

    使用场景: 在项目开发中,后端需要处理一连串的逻辑,或者等待第三方的数据返回来进行处理之后在返回给前端,可能时间会很长,而且前端也不知道后端什么时候能处理好(时间长的话会达到10分钟左右),如果采用普 ...

  8. vue标签内循环数据逗号分隔

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. 如何在vue项目中引入element-ui

    安装 elementUI npm install element-ui --save 引入elementUI import ElementUI from 'element-ui' import 'el ...

  10. 分布式系统session一致性解决方案

    在单机系统中,不存在Session共享问题,但是在分布式系统中,我们必须实现session共享机制,使得多台应用服务器之间会话统一,如果不进行Session共享会出现数据不一致,比如:会导致请求落到不 ...