树形dp的进阶 (一)
①树的重心的性质的运用
②缩点以后寻找规律 树的直径!
③树形dp上的公式转换
④和期望有关的树形dp + 一点排列组合的知识
⑤
⑥
⑦
⑧
⑨
⑩
一:Codeforces Round #364 (Div. 1) B
http://codeforces.com/problemset/problem/700/B
题目大意:给你一棵树,给你k个树上的点对。找到k/2个点对,使它在树上的距离最远。问,最大距离是多少?
思路:我们可以把树上的这个分成两个集合,然后两边的点的数目相等。符合这个条件的就是树的重心,所以我们只需要找到树的中心就行啦。
//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
/*
题目大意:
给你一棵树,给你k个树上的点对。找到k/2个点对,使它在树上的距离最远。
问,最大距离是多少?
*/
const int maxn = + ;
int n, k;
vector<int> G[maxn];
bool vis[maxn];
int dp_cnt[maxn]; int dfs_cnt(int u, int fa){
int cnt = vis[u];
for (int i = ; i < G[u].size(); i++){
int v = G[u][i];
if (v == fa) continue;
cnt += dfs_cnt(v, u);
}
return dp_cnt[u] = cnt;
} void dfs_ce(int u, int fa, int &ce, int &maxcnt, int treesize){
int tmp = treesize - dp_cnt[u];
for (int i = ; i < G[u].size(); i++){
int v = G[u][i];
if (v == fa) continue;
tmp = max(tmp, dp_cnt[v]);
dfs_ce(v, u, ce, maxcnt, treesize);
}
if (maxcnt > tmp){
maxcnt = tmp; ce = u;
}
} LL ans;
void dfs(int u, int fa, int len){
if (vis[u]) {
ans = 1LL * len + ans;
}
for (int i = ; i < G[u].size(); i++){
int v = G[u][i];
if (v == fa) continue;
dfs(v, u, len + );
}
} int main(){
scanf("%d%d", &n, &k);
for (int i = ; i <= k * ; i++){
int u; scanf("%d", &u);
vis[u] = true;
}
for (int i = ; i < n; i++){
int u, v; scanf("%d%d", &u, &v);
G[u].pb(v); G[v].pb(u);
}
int treesize = dfs_cnt(, -);
int cetroid, maxcnt = maxn;
dfs_ce(, -, cetroid, maxcnt, treesize);
dfs(cetroid, -, );
printf("%lld\n", ans);
return ;
}
关键:寻找题目关键问题所在(分成两个数目相同的点集),然后探究树的性质
二:http://codeforces.com/contest/734/problem/E
题目大意:给你一棵树,树上每个点都是黑色或者是白色,每次有一个操作,选取一个点,把周围和它相邻的所有点的颜色都翻转一次,问最少需要几次操作才能让这棵树变成同一种颜色?
思路:我刚开始以为就是单纯的树形dp的,于是我刚开始定义dp(i,j)表示i下面的所有子树都变成颜色j需要的最少操作次数。然而发现状态转移的时候完全转移不了。然后表示虽然想到了缩点,但是感觉我这个dp定义的没有什么问题呀,然后就死在这里了,2333
看了一下官方题解,官方题解上面说:缩点以后,我们可以发现,每次操作以后再缩点,至少可以让反转以后再缩点的树和之前的树相比,结点数至少少了2.
然后我们再次发现,缩点所需要的最多次数,一定是直径上面点的个数。然后我们发现缩点的次数最少操作次数一定是>=(d+1)/2的,所以就可以很轻松的用两次dfs遍历找到直径了。
//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 2e5 + ;
int n, pos, maxdeep, ans;
vector<int> G[maxn];
int color[maxn], dp[maxn]; int dfs(int u, int fa, int deep){
if (deep > maxdeep){
maxdeep = deep; pos = u;
}
for (int i = ; i < G[u].size(); i++){
int v = G[u][i];
if (v == fa) continue;
dfs(v, u, deep + (color[u] != color[v]));
}
} void dfs_dia(int u, int fa, int deep){
if (deep > ans) ans = deep;
for (int i = ; i < G[u].size(); i++){
int v = G[u][i];
if (v == fa) continue;
dfs_dia(v, u, deep + (color[u] != color[v]));
}
} int main(){
cin >> n;
for (int i = ; i <= n; i++) scanf("%d", color + i);
for (int i = ; i < n; i++){
int u, v; scanf("%d%d", &u, &v);
G[u].pb(v), G[v].pb(u);
}
dfs(, -, );
dfs_dia(pos, -, );
printf("%d\n", (ans + ) / );
return ;
}
三:zstu oj 4248 链接:戳这里
题目大意:给你一棵以1位根的,边有权值的树,权值定为cost,每个点也有一个val。定义dis(i,j)表示i~j的所有路径权值和。如果存在dis(i,j) < val(i)-val(j),那么所有j的子树都被减去。问最后还有多少个节点?
思路:
设path(u)指从根到u这个节点的边权和,
dis(u,v) = path(v)-path(u) < val(u)-val(v)
val(v)<val(u)+path(u)-Path(v)
所以我们从根开始搜只要一路维护一个max{val(u)+path(u)}即可
关键:公式转化,讲道理应该很快要想到O(n)的方法,然后这个公式一定是和前面传下来的数值有关的(唉,我好菜啊)
//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1e5 + ;
struct Node{
int to; LL cost;
Node(int to = , LL cost = ): to(to), cost(cost){}
};
vector<Node> G[maxn];
int dp[maxn];
LL val[maxn], path[maxn];
int n; int dfs_cnt(int u, int fa, LL p){
dp[u] = , path[u] = p;
for (int i = ; i < G[u].size(); i++){
int v = G[u][i].to;
if (v == fa) continue;
dp[u] += dfs_cnt(v, u, p + G[u][i].cost);
}
return dp[u];
} int dfs(int u, int fa, LL maxval){
int cnt = ;
for (int i = ; i < G[u].size(); i++){
int v = G[u][i].to;
if (v == fa) continue;
if (maxval > path[v] + val[v]) cnt += dp[v];
else cnt += dfs(v, u, path[v] + val[v]);
}
return cnt;
} int main(){
int t; cin >> t;
while (t--){
memset(path, , sizeof(path));
scanf("%d", &n);
for (int i = ; i <= n; i++) G[i].clear();
for (int i = ; i < n; i++){
int u, v; LL c;
scanf("%d%d%lld", &u, &v, &c);
G[u].pb(Node(v, c)); G[v].pb(Node(u, c));
}
for (int i = ; i <= n; i++)
scanf("%lld", val + i);
dfs_cnt(, -, );
int ans = n - dfs(, -, val[] + path[]);
printf("%d\n", ans);
}
return ;
}
四: 链接:戳这里
题目大意:给你一棵有n个节点的树,根是1,每次我们都从1出发,并且走到他的儿子的可能性都是随机的。每一个儿子的权值是根据走的不同的路径来计算的,走的路径不同,儿子的权值就不同(这一点用cf里面的那个代码来表示)
let starting_time be an array of length n
current_time = 0
dfs(v):
current_time = current_time + 1
starting_time[v] = current_time
shuffle children[v] randomly (each permutation with equal possibility)
// children[v] is vector of children cities of city v
for u in children[v]:
dfs(u)
问,最后请计算每一个节点位置的权值。
思路:
其实单单的看到这道题我是很害怕的,因为我很害怕这种求什么期望啊这类的问题。但是感觉概率论这门课上了以后,感觉对数学期望有了一个新的认识,貌似不是那么怕了,然后推导了一下式子以后发现,这道题并没有和我想象中一样那么难。
首先,我们计算出每个节点,他下面的子节点的个数,然后我是列出了样例一中的②、④、⑥、③这五个节点的数学期望的计算方式。然后我们可以得到一个数学期望的公式
目前节点的数学期望 = 父亲节点的数学期望+1+(父亲节点所有孩子的节点和 - 目前节点的size)/2.
该公式来的过程如下:
假定父亲节点是fa,父亲节点下面儿子的个数为m,父亲节点的下面的晚辈的总个数(包括自身)是fasize,儿子节点为child,儿子节点的下面晚辈的总个数(包括自身)是childsize,儿子节点下面的孙子的个数为n。
然后目前我们可以发现,当前我们停留的点为child,那么,child到fa这条路经过的次数一定是2^(n-1)次,所以,除了目前这个child外,其他儿子节点每个的贡献次数都为2^n-2次,所以我们得到如下的递推式:
父亲节点的期望值+(2^(n-1) + (其他儿子的size和) * 2^(n-2)) / (2^(n-1)),因此就得到上面的递推式啦
//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1e5 + ;
vector<int> road[maxn];
int n;
LL tree[maxn];///包括当前节点
double res[maxn]; LL dfs_size(int u){
tree[u] = ;
for (int i = ; i < road[u].size(); i++){
tree[u] += dfs_size(road[u][i]);
}
return tree[u];
} void dfs_ans(int u, int fa){
res[u] = 1.0 + res[fa];
if (fa != ) res[u] += 1.0 * (tree[fa] - - tree[u]) / 2.0;
for (int i = ; i < road[u].size(); i++)
dfs_ans(road[u][i], u);
} int main(){
cin >> n;
for (int i = ; i <= n; i++){
int x; scanf("%d", &x);
road[x].push_back(i);
}
dfs_size();
dfs_ans(, );
for (int i = ; i <= n; i++){
printf("%.6f\n", res[i]);
}
return ;
}
关键:熟悉数学期望,掌握排列组合
五:
六:
七:
八:
九:
十:
树形dp的进阶 (一)的更多相关文章
- 算法进阶面试题05——树形dp解决步骤、返回最大搜索二叉子树的大小、二叉树最远两节点的距离、晚会最大活跃度、手撕缓存结构LRU
接着第四课的内容,加入部分第五课的内容,主要介绍树形dp和LRU 第一题: 给定一棵二叉树的头节点head,请返回最大搜索二叉子树的大小 二叉树的套路 统一处理逻辑:假设以每个节点为头的这棵树,他的最 ...
- [提升性选讲] 树形DP进阶:一类非线性的树形DP问题(例题 BZOJ4403 BZOJ3167)
转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7337179.html 树形DP是一种在树上进行的DP相对比较难的DP题型.由于状态的定义多种多样,因此解法也五 ...
- 浅谈关于树形dp求树的直径问题
在一个有n个节点,n-1条无向边的无向图中,求图中最远两个节点的距离,那么将这个图看做一棵无根树,要求的即是树的直径. 求树的直径主要有两种方法:树形dp和两次bfs/dfs,因为我太菜了不会写后者这 ...
- 题解 poj3585 Accumulation Degree (树形dp)(二次扫描和换根法)
写一篇题解,以纪念调了一个小时的经历(就是因为边的数组没有乘2 phhhh QAQ) 题目 题目大意:找一个点使得从这个点出发作为源点,流出的流量最大,输出这个最大的流量. 以这道题来介绍二次扫描和换 ...
- 初涉树形dp
算是一个……复习以及进阶? 什么是树形dp 树形dp是一种奇妙的dp…… 它的一个重要拓展是和各种树形的数据结构结合,比如说在trie上.自动机上的dp. 而且有些时候还可以拓展到环加外向树.仙人掌上 ...
- Luogu P2458 [SDOI2006]保安站岗【树形Dp】
题目描述 五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序. 已知整个地下超市的所有通道呈一棵树的形状:某些通道之间可以互 ...
- poj3417 LCA + 树形dp
Network Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4478 Accepted: 1292 Descripti ...
- COGS 2532. [HZOI 2016]树之美 树形dp
可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...
- 【BZOJ-4726】Sabota? 树形DP
4726: [POI2017]Sabota? Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 128 Solved ...
随机推荐
- “Hello World!”团队第五周第三次会议
今天是我们团队“Hello World!”团队第五周召开的第三次会议. 双十一大家过的怎么样?由于组内其他成员被“剁手”,今日会议记录由我来写. 博客内容: 一.会议时间 二.会议地点 三.会议成员 ...
- texbbox,combobox设置属性
--输入框 $("#xx").textbox('setValue','value'); //设置输入框的值 $('#xx').textbox('textbox').attr('r ...
- zabbix简介
(一)监控系统 初探 前言: 对于监控系统而言,首先必须搞清楚需要监控什么? (1)硬件设备和软件设备:服务器,路由器,交换机,I/O存储系统,操作系统,网络,各种应用程序 (2)各种指标:数据库宕机 ...
- PHP对象类型转换
其他数据类型转换为对象类型 其他数据类型转换为对象类型,得到的结果是:内置标准类(stdclass)的一个对象! 语法形式为: $obj1 = (object) 其他类型数据: 数组转换为对象:数 ...
- .net MVC中使用angularJs刷新页面数据列表
使用angularjs的双向绑定功能,定时刷新页面上数据列表(不是刷新网页,通过ajax请求只刷新数据列表部分页面),实例如下: @{ Layout = null; } <!DOCTYPE ht ...
- asp.netMVC中权限控制论
这里设想了一个简单的思路,如果用户登录了,也就是session中有值才可以在控制器中操作,这样先添加一个控制器,如下代码: public class AuthController : Controll ...
- Appium自动化测试框架
1.在utils包中创建一个AppiumUtil类,这个类是对appium api进行封装的. 代码如下: package utils; import java.net.MalformedURLExc ...
- presence_of_element_located与visibility_of_element_located区别
selenium 问题:加了显性等待后,操作元素依然出错 背景: 用WebDriverWait时,一开始用的是presence_of_element_located,我对它的想法就是他就是用来等待 ...
- p12转pem公钥私钥
cer格式证书生成p12文件,前面写了有一篇了. 这里是从p12文件导出公钥和私钥 //1.生成1.key文件 openssl pkcs12 -in apple_payment.p12 -nocert ...
- BZOJ3743 COCI2015Kamp(树形dp)
设f[i]为由i开始遍历完子树内所要求的点的最短时间,g[i]为由i开始遍历完子树内所要求的点最后回到i的最短时间.则g[i]=Σ(g[j]+2),f[i]=min{g[i]-g[j]+f[j]-1} ...