HDU 4123 Bob’s Race(树形DP+RMQ)



题目大意:给定一棵树,每一个点都从当前位置走到距离最远的位置,1~n的连续区间中最大并且走的最远距离差值不超过Q的区间右多大。

思路:首先求出每个点能到达的最远距离,这一步有两种做法:

第一种做法是树形DP,两次dfs,第一次求出从每个节点不同儿子节点所能走到最远距离maxlen[0],次远距离maxlen[1],以及走到最远距离时经过的儿子节点maxid。第二次dfs,每个点u能到达的最远距离就是不在u子树上的结点和u之间的最远距离len 与 u到子孙节点的最远距离中的最大值,在dfs的过程中更新len ,这就要用到我们第一次求得的maxid,假如当前儿子是maxid,当前边权值是w,那么儿子的len就为len+w,否则len为max(len,maxlen[0])+w.这一种做法代码量较小,但思路相对复杂。

第二种做法,考虑树的直径,不难发现所有点能到达的最远点一定为直径的两个端点之一,那么我们先两次dfs求出直径再两次dfs计算出所有点能到达的最远距离。这种做法相对好想。

求出了所有点能到的最远距离后我们初始化rmq,然后用对于每个询问用尺取法O(n)扫一遍,具体就是用两个指针一个指向起点,一个指向终点,如果当前差值大于q那么头指针后移否则尾指针后移并更新答案。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 50500;
const int INF = 0x3f3f3f3f;
int n, m;
int maxlen[MAXN][2], maxid[MAXN], dist[MAXN];
struct Edge{
    int to,next;
    int w;
} edge[MAXN*2];
int head[MAXN],tot;
void init() {
    tot = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w) {
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void dfs1(int cur, int fa) {
	maxlen[cur][0]=0, maxid[cur]=cur, maxlen[cur][1]=-1; 
	for(int i = head[cur]; i != -1; i = edge[i].next) {
		int u = edge[i].to, w = edge[i].w;
		if(u == fa) continue;
		dfs1(u, cur);
		if(maxlen[u][0]+w > maxlen[cur][0]) {
			maxlen[cur][1] = maxlen[cur][0];
			maxlen[cur][0] = maxlen[u][0] + w;
			maxid[cur] = u;
		}
		else if(maxlen[u][0]+w > maxlen[cur][1]) maxlen[cur][1]=maxlen[u][0]+w;
	}
} 
void dfs2(int cur, int fa, int len) {  //len为cur的非子树到cur的最远距离 
	dist[cur] = max(maxlen[cur][0], len);
	for(int i = head[cur]; i != -1; i = edge[i].next) {
		int u = edge[i].to, w = edge[i].w;
		if(u == fa) continue;
		if(u == maxid[cur]) dfs2(u, cur, max(maxlen[cur][1],len)+w);
		else dfs2(u, cur, dist[cur]+w);
	}
}
int dmin[MAXN][20], dmax[MAXN][20];
void RMQ_init() {
	for(int i = 1; i <= n; i++) dmin[i][0] = dmax[i][0] = dist[i];
	for(int j = 1; (1<<j) <= n; j++) {
		for(int i = 1; i+(1<<j)-1 <= n; i++) {
			dmin[i][j] = min(dmin[i][j-1], dmin[i+(1<<(j-1))][j-1]);
			dmax[i][j] = max(dmax[i][j-1], dmax[i+(1<<(j-1))][j-1]);
		}
	}
}
int RMQ(int L, int R) {
	int k = 0;
	while((1<<(k+1)) <= R-L+1) k++;
	return max(dmax[L][k], dmax[R-(1<<k)+1][k])-min(dmin[L][k], dmin[R-(1<<k)+1][k]);
}
int main() {
    //freopen("input.txt", "r", stdin);
	while(scanf("%d%d", &n, &m)==2 && n) {
		init();
		for(int i = 1; i < n; i++) {
			int u, v, w; scanf("%d%d%d", &u, &v, &w);
			addedge(u, v, w);
			addedge(v, u, w);
		}
		dfs1(1, 0);
		dfs2(1, 0, 0);
		RMQ_init();
		while(m--) {
			int q; scanf("%d", &q);
			int ed=0, ans=0;
			for(int st = 1; st <= n; st++) {
				if(ed == n) continue;
				while(ed<n && RMQ(st, ed+1)<=q) ed++, ans=max(ans, ed-st+1);
			}
			printf("%d\n", ans);
		}
	}
    return 0;
}

















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值