【HAOI2015】树上染色

【题目描述】
有一棵点数为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围绕点来进行,首先你无法定义一种状态,其次你不方便转移

所以我们考虑一条边能产生的贡献

首先一条边能产生的贡献为:

   Wi=dis×((这条边左边的黑点×这条边右边的黑点)+(这条边左边的白点×这条边右边的白点))

我们考虑一对点产生的贡献,这一条边L左侧的黑点要想连接L右侧的黑点,必定经过L,所以L会有贡献

有几对这样的黑点,边L就被经过几次,白点同理,所以得到了上面的方程

我们设f[i][j]表示以i为根节点的子树中染j个黑点的最大收益,

size[x]表示以x为根的子树大小,目标:f[1][k];

设当前搜索到以x为根的子树,设son为x的一个儿子,则:

  $f[x][j]=max(f[x][p]+f[son][j-p]+Wi),$

  $j<=min(size[x],k),p<=min(size[son],k)$

Wi表示x与son所连边产生的贡献,在上面已经解释过了

更新f的时候,如果正着推,就需要把f存到一个临时数组里;循环完后再赋值给f,如果倒着枚举直接更新就好了

当然你还可以减少枚举的数量,即j<=min(size[x],k),p<=min(size[son],k)。

下面给出两份代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MAXN 2005
using namespace std;
ll n,k;
ll fr[MAXN<<],to[MAXN<<],nxt[MAXN<<],pre[MAXN],cnt=,dis[MAXN<<];
void add(ll u,ll v,ll w){
cnt++,fr[cnt]=u,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt,dis[cnt]=w;
}
ll f[MAXN][MAXN],temp[MAXN],size[MAXN];
void dfs(ll x,ll fa){
size[x]=;
for(ll i=pre[x];i;i=nxt[i]){
ll y=to[i];
if(y==fa) continue;
dfs(y,x);
ll num_b1=min(k,size[x]),num_b2=min(k,size[y]),l;
memset(temp,,sizeof(temp));
for(ll j=;j<=num_b1;j++){
for(ll p=;p<=num_b2&&p+j<=k;p++){
l=dis[i]*((k-p)*p+(size[y]-p)*(n-k-size[y]+p));
temp[j+p]=max(temp[j+p],f[x][j]+f[y][p]+l);
}
}
ll m=min(num_b1+num_b2,k);
for(ll j=;j<=m;j++)
f[x][j]=temp[j];
size[x]+=size[y];
}
}
int main(){
scanf("%lld%lld",&n,&k);
for(ll i=,u,v,d;i<n;i++){
scanf("%lld%lld%lld",&u,&v,&d);
add(u,v,d),add(v,u,d);
}
dfs(,);
printf("%lld\n",f[][k]);
return ;
}

正序

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define MAXN 2005
using namespace std;
ll n, k;
ll to[MAXN << ], nxt[MAXN << ], pre[MAXN], cnt = , dis[MAXN << ];
void add(ll u, ll v, ll w) { cnt++, to[cnt] = v, nxt[cnt] = pre[u], pre[u] = cnt, dis[cnt] = w; }
ll f[MAXN][MAXN], temp[MAXN][MAXN], size[MAXN];
void dfs(ll x, ll fa) {
size[x] = ;
for (ll i = pre[x]; i; i = nxt[i]) {
ll y = to[i];
if (y == fa)
continue;
dfs(y, x);
ll num_b1 = min(k, size[x]), l;
for (ll j = num_b1; j >= ; j--) {
ll num_b2 = min(k - j, size[y]);
for (ll p = num_b2; p >= ; p--) {
l = dis[i] * ((k - p) * p + (size[y] - p) * (n - k - size[y] + p));
f[x][j + p] = max(f[x][j + p], f[x][j] + f[y][p] + l);
}
}
size[x] += size[y];
}
}
int main() {
scanf("%lld%lld", &n, &k);
for (ll i = , u, v, d; i < n; i++) {
scanf("%lld%lld%lld", &u, &v, &d);
add(u, v, d), add(v, u, d);
}
dfs(, );
printf("%lld\n", f[][k]);
return ;
}

倒序,感谢loj码风优化

【HAOI2015】树上染色—树形dp的更多相关文章

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

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

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

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

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

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

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

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

  5. bzoj4033 [HAOI2015]树上染色——树形DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4033 树形DP,状态中加入 x 与父亲之间的边的贡献: 边权竟然是long long... ...

  6. BZOJ 4033 [HAOI2015]树上染色 ——树形DP

    可以去UOJ看出题人的题解. 这样的合并,每一个点对只在lca处被考虑到,复杂度$O(n^2)$ #include <map> #include <ctime> #includ ...

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

    题目描述 给你一棵\(n\)个点的树,你要把其中\(k\)个点染成黑色,剩下\(n-k\)个点染成白色.要求黑点两两之间的距离加上白点两两之间距离的和最大.问你最大的和是多少. \(n\leq 200 ...

  8. 【HAOI2015】树上染色 - 树形 DP

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

  9. BZOJ_4033_[HAOI2015]树上染色_树形DP

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

随机推荐

  1. [JZOJ2865]【集训队互测 2012】Attack

    题目 题目大意 平面上有一堆带权值的点.两种操作:交换两个点的权值,查找一个矩形的第\(k\)小 \(N<=60000\) \(M<=10000\) \(10000ms\) 思考历程&am ...

  2. 网页存储倒计时与解决网页cookie保存多个相同key问题

    短信倒计时多用网页临时存储,这可以保证网页在关闭状态也可记时. <p class="test_button" id="getcode">获取验证码& ...

  3. 字符串——cf1109B

    /* 先判不可行的情况:n/2的是单一字符 判只切割一次能不能组成回文 枚举每个切割点,交换两个串的位置 剩下就是割两次 */ #include<bits/stdc++.h> #inclu ...

  4. DNS的解析过程

    1.什么是DNS 在互联网上,唯一标识一台计算机的是IP地址,但是IP地址不方便记忆,通过一个域名对应一个IP地址,来达到找到IP地址的目的,那么DNS就是将域名转换成IP地址的过程. 2.DNS查询 ...

  5. 多线程--GIL锁

    GIL 即全局解释器锁,是一个互斥锁,防止多个线程在同一时间执行python代码,因为在一个python进程中,不仅有主线程而且还有该主线程开启的子线程,还有解释器开启的垃圾回收机等解释器级别的线程. ...

  6. npm使用入门

    NPM使用入门 npm 就是node package manager node的包管理工具 我们通过npm install 模块 来安装模块,缩写:npm i 模块,注意,低版本的node可能需要np ...

  7. 前后端分离+本地服务实时刷新+缓存管理+接口proxy+静态资源增量更新+各种性能优化+上线运维发布——gulp工作流搭建

    技巧集:http://www.gulpjs.com.cn/docs/recipes/ 其实无非就是利用各种gulp插件.node脚本对项目文件做各种IO操作,只是备忘,需要的话,还是自己重新写最合适. ...

  8. 第四周课堂笔记4th

    编码     Ascii美国 一个字节表示一个字符,必能表示汉子 大写字母65-90  小写字母97-122 265个位置 8位表示一个字节,  8bit=1byte GBK  中国 只包含本国文字 ...

  9. C#窗体代码相关笔记

    获取ComboBox下拉列表的所有选项值 ArrayList al = new ArrayList(); foreach (string item in this.comboBox2.Items) { ...

  10. mybatis 查询sql时foreach使用法

    找到俩个例子摘下来 sql查询用户in传list参数 <select id="getEmpsByConditionForeach" resultType="com. ...