简介
如果需要求出无根树上每一个点的答案,且一般树形dp只能一次求解(一般)一个点答案时,往往可以考虑二次换根。
基本思想
因为树形dp时以指定根为根的树内某节点子树的贡献会被计算,往往考虑使用已计算得到的这些贡献来求出其他点的答案(一般会在第一次计算出被指定为根的点的答案)
例题
HDU-2196
分析
题目要求求出每个点在树内的最长距离。每个点dfs会导致O(\(n^2\))复杂度。我们设f[i]为指定根树内节点i在其此时子树内能到达的最远距离,g[i]表示i在外部树能达到的最远距离,答案即是\(max(f[i],g[i])\)。首先我们可以一遍dfs处理出f[i]。处理g[i]时,我们是用i来更新它的子节点的(那i谁来更新i?当然是i的da.i没有da怎么办?n那么它就是指定根,指定根答案已被计算...)。首先找到更新f[i]的节点xb1,用f[xb1] + dis{xb1,i}更新子树内其他所有点的g[];并求出da的次长距离,更新g[xb1],以上求出了在i子树范围内个子节点能到达的最远距离;然后再考虑i外部子树对子节点的贡献--用g[x]更新即可,还要递归处理其他节点。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <bitset>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define ll long long
#define rint register int
#define mid ((L + R) >> 1)
#define lson (x << 1)
#define rson (x << 1 | 1)
using namespace std;
template<typename xxx>inline void read(xxx &x) {
x = 0;int f = 1;char c = getchar();
for(;c ^ '-' && !isdigit(c);c = getchar());
if(c == '-') f = -1,c = getchar();
for(;isdigit(c);c = getchar()) x = (x << 3) + (x << 1) + (c ^ '0');
x *= f;
}
template<typename xxx>inline void print(xxx x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int maxn = 20010;
const int mod = 1e9 + 7;
const int inf = 0x7f7f7f7f;
struct edge{
int to,val,last;
}e[maxn];
int head[maxn],tot;
inline void add(int from,int to,int val) {
++tot;
e[tot].to = to;
e[tot].val = val;
e[tot].last = head[from];
head[from] = tot;
}
int f[maxn];//f[i]表示i在以i为根的子树内的到叶节点的最大距离
int g[maxn];//g[i]表示i在以i为根的子树外的最远距离
inline void ddfs2(int x,int da) {//以x更新x子树的g
int xb1 = 0,v1 = 0;
int xb2 = 0,v2 = 0;
for(rint i = head[x];i;i = e[i].last) {
if(e[i].to ^ da) {
if(f[e[i].to] + e[i].val >= f[xb1] + v1) xb1 = e[i].to,v1 = e[i].val;//找f[x]的更新来源 ,也是找x在子树中最长
}
}
for(rint i = head[x];i;i = e[i].last) {
if(e[i].to ^ da && e[i].to ^ xb1) {
g[e[i].to] = f[xb1] + v1 + e[i].val;
if(f[e[i].to] + e[i].val >= f[xb2] + v2) xb2 = e[i].to,v2 = e[i].val;//找x在子树中次长
}
}
g[xb1] = f[xb2] + v1 + v2;//次长更新最长,以上处理完x所有子节点在子树内的g
for(rint i = head[x];i; i = e[i].last) {
if(e[i].to ^ da) {
if(g[e[i].to] < g[x] + e[i].val) g[e[i].to] = g[x] + e[i].val;//更新了子树对g的贡献还应更新x外部树对x子树的贡献
ddfs2(e[i].to,x);
}
}
return ;
}
inline void ddfs1(int x,int da) {
for(rint i = head[x];i;i = e[i].last) {
if(e[i].to ^ da) {
ddfs1(e[i].to,x);
if(f[x] < f[e[i].to] + e[i].val) f[x] = f[e[i].to] + e[i].val;
}
}
return ;
}
int n;
int main() {
while(~scanf("%d",&n)) {
tot = 0;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(head,0,sizeof(head));
for(rint i = 2;i <= n; ++i) {
int a,b;
read(a);read(b);
add(i,a,b);
add(a,i,b);
}
f[0] = g[0] = -inf;
ddfs1(1,0);
ddfs2(1,0);//我们以1为根,则1的答案即是为f[1]
for(rint i = 1;i <= n; ++i) print(Max(f[i],g[i])),putchar('\n');
}
}
/*
*/