①树的重心的性质的运用

②缩点以后寻找规律  树的直径!

③树形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的进阶 (一)的更多相关文章

  1. 算法进阶面试题05——树形dp解决步骤、返回最大搜索二叉子树的大小、二叉树最远两节点的距离、晚会最大活跃度、手撕缓存结构LRU

    接着第四课的内容,加入部分第五课的内容,主要介绍树形dp和LRU 第一题: 给定一棵二叉树的头节点head,请返回最大搜索二叉子树的大小 二叉树的套路 统一处理逻辑:假设以每个节点为头的这棵树,他的最 ...

  2. [提升性选讲] 树形DP进阶:一类非线性的树形DP问题(例题 BZOJ4403 BZOJ3167)

    转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7337179.html 树形DP是一种在树上进行的DP相对比较难的DP题型.由于状态的定义多种多样,因此解法也五 ...

  3. 浅谈关于树形dp求树的直径问题

    在一个有n个节点,n-1条无向边的无向图中,求图中最远两个节点的距离,那么将这个图看做一棵无根树,要求的即是树的直径. 求树的直径主要有两种方法:树形dp和两次bfs/dfs,因为我太菜了不会写后者这 ...

  4. 题解 poj3585 Accumulation Degree (树形dp)(二次扫描和换根法)

    写一篇题解,以纪念调了一个小时的经历(就是因为边的数组没有乘2 phhhh QAQ) 题目 题目大意:找一个点使得从这个点出发作为源点,流出的流量最大,输出这个最大的流量. 以这道题来介绍二次扫描和换 ...

  5. 初涉树形dp

    算是一个……复习以及进阶? 什么是树形dp 树形dp是一种奇妙的dp…… 它的一个重要拓展是和各种树形的数据结构结合,比如说在trie上.自动机上的dp. 而且有些时候还可以拓展到环加外向树.仙人掌上 ...

  6. Luogu P2458 [SDOI2006]保安站岗【树形Dp】

    题目描述 五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序. 已知整个地下超市的所有通道呈一棵树的形状:某些通道之间可以互 ...

  7. poj3417 LCA + 树形dp

    Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4478   Accepted: 1292 Descripti ...

  8. COGS 2532. [HZOI 2016]树之美 树形dp

    可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...

  9. 【BZOJ-4726】Sabota? 树形DP

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved ...

随机推荐

  1. CS小分队第一阶段冲刺站立会议(5月12日)

    昨日成果:2048整体界面效果经组员韩雪冬美化之后档次提升了好几个,我为其添加了保存并显示最高分数的功能. 遇到困难:当我想把access数据库由accdb改成mdb时,发生未知错误 ,导致数据库无法 ...

  2. Alpha冲刺——第十天

    Alpha第十天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...

  3. Codeforces Round #345 (Div. 1) C. Table Compression dp+并查集

    题目链接: http://codeforces.com/problemset/problem/650/C C. Table Compression time limit per test4 secon ...

  4. alpha冲刺(事后诸葛亮)

    [设想和目标] Q1:我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? "小葵日记"是为了解决18-30岁年轻用户在记录生活时希望得到一美体验友好 ...

  5. BOM对象属性定时器的调用

    使count中的内容,自动切换 <body> <h1 id="count"></h1> </body> //获取count var ...

  6. (九) 使用Jmeter 做分布式压测 ;

    在使用Jmeter进行性能测试时,如果并发数比较大(比如最近项目需要支持1000并发),单台电脑的配置(CPU和内存)可能无法支持,这时可以使用Jmeter提供的分布式测试的功能. 一.Jmeter分 ...

  7. js & enter

    js & enter keycode function (e) { if (e.which === 13 || e.keyCode === 13) { //code to execute he ...

  8. vue.js+vue-router+webpack keep-alive用法

    本文是机遇 提纲:   现有需求 各个解决方案的优缺点 相关的问题延伸 keep-alive使用详解   现有需求   每个项目中都存在许多列表数据展示页面,而且通常包含一些筛选条件以及分页.   并 ...

  9. BZOJ 1222 产品加工(DP)

    某加工厂有A.B两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成.由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时由两台机器共同进行加工,所完成任 ...

  10. MSSQL数据库分页存储过程

    create procedure [dbo].[p_splitpage] ), , , output, output as set nocount on declare @p1 int ,,@rowc ...