题目链接


题目大意:

给你一颗NNN个节点的树,树上每条边有边权就是遍历的时间,你可以从任意节点出发遍历N−kN-kN−k个点并且回到出发点,问你最短的时间是多少?
k∈[0,min(N,20)],N∈[1,1e4]k\in[0,min(N,20)],N\in[1,1e4]k∈[0,min(N,20)],N∈[1,1e4]


解题思路:

思维误区:一开始我对于这个任意节点出发以为是要换根,但是子父之间的关系不好找!!,而且我定义的状态方程是dp[i][j]dp[i][j]dp[i][j]从iii这个子树里不选jjj个点的花费的最小时间。这个无法更新:因为它一定要联通,我这个方程只表明了不选jjj个点无法保证联通性!!!

其实这个任意点出发是没关系的:假设我们固定一个111号点为根,那么答案肯定是一个连通块,那么连通块里面你从任意一个点出发都可以得到同一个答案,那么我们可以就假设从这个联通块里面深度最小的那个节点出发得到的结果!!

那么为了得到连通块:我们dp[i][j]dp[i][j]dp[i][j]要加上一个限制:就是iii这个子树里面不选jjj个点,并且iii这个点一定要选!! 再加上前面的限制就是并且iii号点一定是连通块里面深度最低的点!!

那么我们dpdpdp就很好转移了:如果有多个子树我们分别枚举在两个子树里面分别不选多少就好了!!

更新答案的时候我们要用全局ansansans去更新:
假设我们现在到了uuu号点,那么我们默认是uuu子树之外的点是不选的

if(n-siz[u]<=k) ans = min(ans,dp[u][k-(n-siz[u])]);

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {read(first);read(args...);
}
vector<PII> G[maxn];
int n,k;
ll dp[maxn][25], ans; // dp[i][j] 表示从 i 这子树里面一定选i这个点并且不选j个点的最小花费
int siz[maxn];
inline void dfs(int u, int fa) {bool tag = 0;siz[u] = 1;for(auto it : G[u]) {if(it.first == fa) continue;dfs(it.first,u);siz[u] += siz[it.first];if(!tag) { // 判断有多少个子树for(int i = 0; i <= min(k,siz[it.first]); ++ i) dp[u][i] = dp[it.first][i] + it.second;if(siz[it.first] <= k) dp[u][siz[it.first]] = 0; tag = 1;} else {ll res[25];for(int i = 0; i <= k; ++ i) res[i] = LLF;for(int j = 0; j <= k; ++ j) // 枚举分别不拿多少个?for(int i = 0; i <= j; ++ i) {if(siz[it.first] <= j - i)res[j] = min(res[j],dp[u][i]);else res[j] = min(res[j],dp[u][i]+dp[it.first][j-i]+it.second); }for(int i = 0; i <= k; ++ i) dp[u][i] = res[i];}}if(n-siz[u]<=k) ans = min(ans,dp[u][k-(n-siz[u])]);if(G[u].size() == 1 && u != 1) dp[u][0] = 0; // 叶子节点更新答案 只有这个是合法的
}int main() {IOS;int _;cin >> _;while(_--) {ans = LLF;cin >> n >> k;for(int i = 0; i <= n+1; ++ i) G[i].clear();for(int i = 1; i < n; ++ i) {int u, v, w;cin >> u >> v >> w;u ++, v ++; // 把编号调到[1,n]G[u].push_back({v,w});G[v].push_back({u,w});}for(int i = 0; i <= n; ++ i)for(int j = 0; j <= 20; ++ j)dp[i][j] = LLF;dfs(1,0);cout << ans * 2ll << "\n";}
}
/*
3
2 0
0 1 3000
4 1
0 1 81
1 2 41
2 3 59
9 2
0 1 1000
1 2 1200
0 3 1000
3 4 1200
0 5 1000
5 6 1200
0 7 1800
7 8 600
*/

树形dp ---- gym101655 D - Delta Quadrant 树上连通块思维换根 + 树形dp相关推荐

  1. 51nod1812树的双直径(换根树DP)

    传送门:http://www.51nod.com/Challenge/Problem.html#!#problemId=1812 题解:头一次写换根树DP. 求两条不相交的直径乘积最大,所以可以这样考 ...

  2. [XXSY] 构树(prufer序列,树上连通块DP)

    传送门 CayleyCayleyCayley公式:一个完全图有nn−2n^{n-2}nn−2棵无根生成树(可用prufer序列证明) 扩展CayleyCayleyCayley公式:被确定边分为大小为a ...

  3. Rikka with Travels【换根树dp】

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6686 不写题解了,写不动 还有其他简单的做法 #include <bits/stdc++.h&g ...

  4. 牛客多校1 - Infinite Tree(虚树+换根dp+树状数组)

    题目链接:点击查看 题目大意:给出一个无穷个节点的树,对于每个大于 1 的点 i 来说,可以向点 i / minvid[ i ] 连边,这里的 mindiv[ x ] 表示的是 x 的最小质因数,现在 ...

  5. 2019长沙学院新生赛(A水,B水,C(整除分块),D水,E(巧数学),F(二分+bfs),H(换根dp),I(线段树)J(dp+倍增+lca))

    A-XOR SUM 通过简单观察得知连续四个数的异或值就是等于0,暴力找出左区间和右区间就可以了,最多跑四个单位 0^1^2^3==0   4^5^6^7=0 #include<bits/std ...

  6. CF 1324-F Maximum White Subtree //树形换根dp

    题目链接 http://codeforces.com/problemset/problem/1324/F 题意 给你一棵树( n n n 个顶点)和一个数组 a i a_i ai​,每个顶点要不是白色 ...

  7. P4827-[国家集训队]Crash 的文明世界【树形dp,换根法,斯特林数】

    正题 题目链接:https://www.luogu.com.cn/problem/P4827 题目大意 一颗nnn个点的树,定义dis(i,j)dis(i,j)dis(i,j)表示树上i,ji,ji, ...

  8. OpenJudge1043 树上游戏(换根dp+细节处理)

    树上游戏 给定一棵 nnn 个节点的树,点从 111 到 nnn 编号,点有点权,边有边权, Alice\text{Alice}Alice 和 Bob\text{Bob}Bob 两人在做游戏. 棋子以 ...

  9. 最大疯子树:树形DP优化:二次扫描+换根法(poj3585)

    相信看这篇文的人应该是会一些简单的线性树形dp的吧-- 如果有不会的请先看看树形dp基础吧--比如这道题没有上司的舞会 其实之所以想写这篇文是因为前段时间被教练骗去叫去参加一场UESTC组织主办的线下 ...

最新文章

  1. php 动态加载扩展,仿照PHP的实现简单的扩展动态加载
  2. 按要求罗列所有字符串字符序列
  3. Python slice() 函数
  4. JEP 277“增强弃用”非常好。 但这是一个更好的选择
  5. [转载] python中字符串编码形式及其所占字节
  6. Linux进不了进程,既然在Linux中程序不是进程,那么到底什么才算是进程呢?
  7. callback回调使用 vue_Vue实现剪切板图片压缩
  8. Android开发案例 点击按钮出现 简易的消息提示框
  9. BOF算法 基于SIFT+KMeans
  10. 邮件服务器3---mx记录及postfix相关文件介绍
  11. 基本光照与阴影(一)
  12. html 绘制矩形,HTML5中使用canvas绘制矩形
  13. 分别解释final,finally,finalize是什么?
  14. 敏捷开发产品管理系列之二:产品版本规划
  15. numpy矩阵删除一列或一行
  16. <Linux开发>linux开发工具- 之-TFTP
  17. CNTK-训练神经网络
  18. 智能驾驶传感器的工作原理
  19. Latex参考文献自动化添加方法
  20. MicroDicom viewer相关

热门文章

  1. python中的异常(try...except...else...finally)
  2. 转载,大佬关于虚拟内存与物理内存关系讲解。
  3. 数据挖掘试题(150道) (1)
  4. ACMNO.49:一元三次方程求解(主要就是精度问题)
  5. Bagging与随机森林算法原理小结
  6. 图像滤波常用算法实现及原理解析
  7. OpenCV形态学处理使用技巧与应用演示
  8. 基于OpenCV的行人目标检测
  9. 智在生活 自在慵懒 科沃斯机器人X京东大牌秒杀日主题展亮相无锡
  10. 大数据在犯罪预防中有独特价值