您的位置:首页 > 其它

hdu 4123 Bob’s Race 树形DP + 单调队列

2012-09-02 18:25 465 查看

Bob’s Race

在昨天的模拟赛中,有一种情况没想明白,就没怎么写了。今天又想了一下,其实和hdu4008很像。然后重新写了一遍就AC了,看来树形DP的题目还得好好写写。

说一下思路:分为求每个点出发的最长路和求最长区间两个阶段。关于求最长区间的可以直接采用单调队列,hdu3530便是一个求最长区间的问题。主要说一下求每个点的出发的最长路。

求最长路:两个DFS. 第一次DFS,我们以任意一个点做为根节点,我采用的是编号为1的节点,求出每个节点在根为1的情况到叶子节点的最长距离和次长距离,要求次长距离与最长距离没有公共边。这里其实就是从节点x的子节点中选出路径最长的与路径次长的。这样就保证了没有公共边。

第二次DFS:变换根节点,也即旋转。将原先非根的节点变成根,当然这里只是为了求出该点出发的最长距离。我们设现在有节点v以及其子节点u.很容易知道经过v的最长路,肯定是从v的子节点走,或者从v的父节点(以第一次DFS的根节点为根的时候)。在算u的距离的时候,我们考虑:如果v的最长路径没有经过u,很显然此时u的最长距离肯定w(u , v) +v的最长距离;假设从u出发到叶子节点的最长距离为d[u] , 当v的最长路t[v] = d[u] + w(u , v) , 此时有可能有两条距离相等的最长路,或者最长路就只有经过u的路径。在前一种情况下,我们也很容易的算的t[u] = t[v] + w , 在后一种情况下,t[u]的值要么是d[u],要么是v的次长路径,不过次长路径是为经过u子树的。分析到这里,我们就知道需要求解出那些数据才能求解出每个点出发的最长距离。在第二次DFS的时候,我们同样的需要求出每个节点出发的最长路径与不相交的次长路径。关于次长路径的计算,参看程序中的解释。

/*
author : csuchenan
PROG   : hdu 4123
LANG   : C++
Algorithm : 树形DP + 单调队列
17	csu_chenan	578MS	3348K	3690B	C++	2012-09-02 17:58:07
*/
#include <cstdio>
#include <cstring>
#include <vector>

#define maxn 50005
#define INF 100000000
#define debug 0
using std::vector ;
struct node{
int f ;
int s ;
node(int a = 0 , int b = 0)
: f(a) , s(b){}
};
vector<node> G[maxn] ;
int qmax[maxn] ;
int qmin[maxn] ;
int d[maxn][2] ;
int t[maxn][2] ;
int n ;
int m ;
int front ;
int tail  ;
int head  ;
int rear  ;

inline void init(){
memset(d , 0 , sizeof(d)) ;
memset(t , 0 , sizeof(t)) ;
for(int i = 0 ; i <= n ; i ++){
G[i].clear() ;
}
}
void swap(int &a , int &b){
a = a ^ b ;
b = a ^ b ;
a = a ^ b ;
}
int max(int x , int y){
return x > y ? x : y ;
}
void dfs1(int v , int fa){
//找出距离中最长的和次长的
for(vector<node>::size_type i = 0 ; i != G[v].size() ; i ++){
int u = G[v][i].f ;
int w = G[v][i].s ;
if(u==fa)
continue ;
dfs1(u , v) ;
if( d[u][0] + w > d[v][1] ){
d[v][1] = d[u][0] + w ;
}
if(d[v][1] > d[v][0]){
swap(d[v][1] , d[v][0]) ;
}
}
}

void dfs2(int v , int fa){

for(vector<node>::size_type i = 0 ; i != G[v].size() ; i ++){
int u = G[v][i].f ;
int w = G[v][i].s ;
if(u == fa)
continue ;

if(t[v][0] > d[u][0] + w){
//v的最长路不经过u点,
t[u][0] = t[v][0] + w ;
t[u][1] = d[u][0] ;
}
else{
//v的最长路可能经过u点的情况
if(t[v][1] == t[v][0]){
//v的次长路与最长路是相等的,
t[u][0] = t[v][0] + w ;
t[u][1] = d[u][0] ;
}
else{
//v的次长路与最长路不相等,必定经过u
if(t[v][1] + w >= d[u][0]){
t[u][0] = t[v][1] + w ;
t[u][1] = d[u][0] ;
}
else{
t[u][0] = d[u][0] ;
t[u][1] = max(t[v][1] + w , d[u][1]) ;
}
}
}
dfs2(u , v) ;
}
}

int work(int q){
int from = 1 ;
front = 1 ;
head  = 1 ;
tail  = 0 ;
rear  = 0 ;
int ans = 0 ;
for(int i = 1 ; i <= n ; i ++){
while(rear >= front && t[qmax[rear]][0] < t[i][0])
rear -- ;
qmax[++ rear] = i ;

while(tail >= head && t[qmin[tail]][0] > t[i][0])
tail -- ;
qmin[++ tail] = i ;

if(t[qmax[front]][0] - t[qmin[head]][0] > q){
if(qmax[front] < qmin[head]){
from = qmax[front] + 1 ;
front ++ ;
}
else{
from = qmin[head] + 1 ;
head ++ ;
}
}
if(ans < i - from + 1)
ans = i - from + 1 ;
}
return ans ;
}

void solve(){
dfs1(1 , 0) ;

t[1][0] = d[1][0] ;
t[1][1] = d[1][1] ;

dfs2(1 , 0) ;
if(debug){
for(int i = 1 ; i <= n ; i ++){
printf("%d %d %d\n" , i , t[i][0] , d[i][0]) ;
}
}

int q ;
int ans ;
for(int i = 0 ; i < m ; i ++){
scanf("%d" , &q) ;
ans = work(q) ;
printf("%d\n" , ans) ;
}
}

int main(){
while(scanf("%d%d" , &n , &m) , n||m){
int a ;
int b ;
int c ;
init() ;
for(int i = 1 ; i < n ; i ++){
scanf("%d%d%d" , &a , &b , &c) ;
G[a].push_back(node(b , c)) ;
G[b].push_back(node(a , c)) ;
}
solve() ;
}
return 0 ;
}






内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: