hdu4123(树形dp,RMQ查询)

202 篇文章 0 订阅
78 篇文章 0 订阅

Bob’s Race

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3088    Accepted Submission(s): 1003


Problem Description
Bob wants to hold a race to encourage people to do sports. He has got trouble in choosing the route. There are N houses and N - 1 roads in his village. Each road connects two houses, and all houses are connected together. To make the race more interesting, he requires that every participant must start from a different house and run AS FAR AS POSSIBLE without passing a road more than once. The distance difference between the one who runs the longest distance and the one who runs the shortest distance is called “race difference” by Bob. Bob does not want the “race difference”to be more than Q. The houses are numbered from 1 to N. Bob wants that the No. of all starting house must be consecutive. He is now asking you for help. He wants to know the maximum number of starting houses he can choose, by other words, the maximum number of people who can take part in his race.
 

Input
There are several test cases.
The first line of each test case contains two integers N and M. N is the number of houses, M is the number of queries.
The following N-1 lines, each contains three integers, x, y and z, indicating that there is a road of length z connecting house x and house y.
The following M lines are the queries. Each line contains an integer Q, asking that at most how many people can take part in Bob’s race according to the above mentioned rules and under the condition that the“race difference”is no more than Q. 

The input ends with N = 0 and M = 0.

(N<=50000 M<=500 1<=x,y<=N 0<=z<=5000 Q<=10000000)
 

Output
For each test case, you should output the answer in a line for each query.
 

Sample Input
  
  
5 5 1 2 3 2 3 4 4 5 3 3 4 2 1 2 3 4 5 0 0
 

Sample Output
  
  
1 3 3 3 5


题意:
先求遍树上的每个点i,能走的最远距离记录为val[i],然后有m个询问,每次询问一个q,求一个最大值减最小值不超过q的最长连续区间。

思路:
先求树的直径两个端点,然后求出每个点到这两个点的距离,有一个结论为点到树上的最远点必为两个端点之一(如果不是的话,那么直径就要换了,反证法),根据此性质求出val数组。
对于每一个询问,扫描一遍,假设j>i,那么以j结尾的最长连续区间的起点必定不小于i的起点(显然),根据这个单调性质,可以用单调队列的思想处理起点,结尾的点枚举即可,判断一个区间内的点是否满足max-min<=q就是用rmq来解决了。
ps:rmq最好预处理lg数组,单调队列只是+n的复杂度。


RMQ:ST算法(Sparse Table):它是一种动态规划的方法。 
以最小值为例。a为所寻找的数组. 
用一个二维数组f(i,j)记录区间[i,i+2^j-1](持续2^j个)区间中的最小值。其中f[i,0] = a[i]; 
所以,对于任意的一组(i,j),f(i,j) = min{f(i,j-1),f(i+2^(j-1),j-1)}来使用动态规划计算出来。 
这个算法的高明之处不是在于这个动态规划的建立,而是它的查询:它的查询效率是O(1). 
假设我们要求区间[m,n]中a的最小值,找到一个数k使得2^k<n-m+1. 
这样,可以把这个区间分成两个部分:[m,m+2^k-1]和[n-2^k+1,n].我们发现,这两个区间是已经初始化好的. 
前面的区间是f(m,k),后面的区间是f(n-2^k+1,k). 
这样,只要看这两个区间的最小值,就可以知道整个区间的最小值! 


不过要注意这题需要扩栈。。。。。不然会超时。。。。。

#pragma comment (linker,"/STACK:102400000,102400000")


#pragma comment (linker,"/STACK:102400000,102400000")
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
typedef long long ll;
const int N=50010;
int len,st,ed;
int m,n;
int maxd[N][2],lg[N],f[N][20],g[N][20],val[N];
int head[N];
int ip;
struct edgenode
{
    int to;
    int w;
    int next;
} tu[100005];
void add(int u,int v,int w)
{
    tu[ip].to=v,tu[ip].w=w,tu[ip].next=head[u],head[u]=ip++;
}

void dfs(int now,int pre,int sum)
{
    if(sum>len)len=sum,st=now;
    for(int k=head[now]; k!=-1; k=tu[k].next)
    {
        int to=tu[k].to;
        if(to!=pre)
            dfs(to,now,sum+tu[k].w);
    }
}

void ddfs(int now,int pre,int sum,int num)
{
    maxd[now][num]=sum;
    for(int k=head[now]; k!=-1; k=tu[k].next)
    {
        int to=tu[k].to;
        if(to!=pre)
            ddfs(to,now,sum+tu[k].w,num);
    }
}

void init_rmq()              // 预处理  O(n*log(n))
{
    int i,j;
    for(i=1; i<=n; i++)
    {
        f[i][0]=g[i][0]=val[i];
    }
    for(j=1; (1<<j)<=n; j++)
    {
        for(i=1; i+j-1<=n; i++)
        {
            if((i+(1<<(j-1))<=n))
            {
                f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);  //避免越界
                g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]);
            }
            else
            {
                f[i][j]=f[i][j-1];
                g[i][j]=g[i][j-1];
            }
        }
    }
}
int query_rmq(int l,int r)
{
    int k=lg[r-l+1];
    return max(f[l][k],f[r-(1<<k)+1][k])-min(g[l][k],g[r-(1<<k)+1][k]);
}

int main()
{
    lg[1]=0;
    for(int i=2; i<=50000; i++)
        lg[i]=lg[i>>1]+1;
    while(~scanf("%d%d",&n,&m)&&m+n)
    {
        ip=0;
        memset(head,-1,sizeof(head));
        for(int i=0; i<n-1; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
            add(b,a,c);
        }
        len=0;
        dfs(1,0,0);
        ed=st;
        len=0;
        dfs(st,0,0);
        ddfs(st,0,0,0);
        ddfs(ed,0,0,1);
        for(int i=1; i<=n; i++)
            val[i]=max(maxd[i][0],maxd[i][1]);
        init_rmq();
        int res,id,ans;
        while(m--)
        {
            int q;
            scanf("%d",&q);
            ans=id=1;
            for(int i=1; i<=n; i++)
            {
                while(id<=i)
                {
                    res=query_rmq(id,i);
                    if(res>q) id++;
                    else break ;
                }
                ans=max(ans,i-id+1);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值