poj 3162(树形dp+线段树|单调队列)

poj 3162

 

题目:

给出n个点,每个点之间又一个距离,这n个点构成一颗树形结构的图。

一个人从第1个点出发,跑到离第1个点最远的地方,跑过的距离为x,依次跑完n个点,此时,每个点都有一个最远的距离,得到最远距离序列a。

找出a序列中一段连续的区间,这个连续的区间内的最大值与最小值之差<=m。求出这个区间的最大长度是多少。

 

思路:

对于每个点i的最长距离,用树形dp求出。

然后不断遍历一段区间[L,R]求出求出这个区间内的最大与最小值,而且满足最大与最小值之差小于m,这个过程用线段树或者

单调队列来维护。

 

线段树实现:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
const int INF = 1e9+10;
int n,head[maxn],tot,id[maxn] = {0};
ll dp[3][maxn],m;
struct Node
{
    int v,nxt,w;
} cur[maxn<<2];
void Init()
{
    memset(head,-1,sizeof(head));
    tot = 0;
}
void Add(int x,int y,int z)
{
    cur[tot].v = y;
    cur[tot].nxt = head[x];
    cur[tot].w = z;
    head[x] = tot++;
}
void dfs1(int rt,int fa)
{
    for(int i=head[rt];i!=-1;i=cur[i].nxt)
    {
        int v = cur[i].v,w = cur[i].w;
        if(fa==v) continue;
        dfs1(v,rt);
        if(dp[0][rt]<dp[0][v]+w){
            dp[1][rt] = dp[0][rt];
            dp[0][rt] = dp[0][v] + w;
            id[rt] = v;
        }
        else if(dp[1][rt]<dp[0][v]+w)
        {
            dp[1][rt] = dp[0][v] + w;
        }
    }
}
void dfs2(int rt,int fa)
{
    for(int i=head[rt];i!=-1;i=cur[i].nxt)
    {
        int v = cur[i].v,w = cur[i].w;
        if(fa==v) continue;
        if(id[rt]==v)
        {
            dp[2][v] = max(dp[1][rt],dp[2][rt]) + w;
        }
        else{
            dp[2][v] = max(dp[0][rt],dp[2][rt]) + w;
        }
        dfs2(v,rt);
    }
}

int tmin[maxn<<2],tmax[maxn<<2];
void build(int rt,int l,int r)
{
    if(l==r)
    {
        tmin[rt] = tmax[rt] = max(dp[0][l],dp[2][l]);
        return ;
    }
    int mid = (l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    tmin[rt] = min(tmin[rt<<1],tmin[rt<<1|1]);
    tmax[rt] = max(tmax[rt<<1],tmax[rt<<1|1]);
}
int mx,mi;
void query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
    {
        mx = max(mx,tmax[rt]);
        mi = min(mi,tmin[rt]);
        return ;
    }
    int mid = (l+r)>>1;
    if(L<=mid)
        query(rt<<1,l,mid,L,R);
    if(R>mid)
        query(rt<<1|1,mid+1,r,L,R);
}
int main(void)
{
    while(~scanf("%d%d",&n,&m))
    {
        Init();
        dp[0][1] = dp[1][1] = dp[2][1] = 0;
        id[1] = -1;
        for(int i=2; i<=n; i++)
        {
            int y,z;
            scanf("%d%d",&y,&z);
            Add(i,y,z);
            Add(y,i,z);
            dp[0][i] = dp[1][i] = dp[2][i] = 0;
            id[i] = -1;
        }
        dfs1(1,-1);
        dfs2(1,-1);
        build(1,1,n);
        int l = 1,r = 1,ans = 0;
        while(r<=n)
        {
            mi = INF;
            mx = 0;
            query(1,1,n,l,r);
            if(mx-mi<=m)
            {
                ans = max(ans,(r-l+1));
                r++;
            }
            while(mx-mi>m)
            {
                l++;
                mi = INF;
                mx = 0;
                query(1,1,n,l,r);
            }
        }
        printf("%d\n",ans);
    }

    return 0;
}

 

单调队列:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
const int maxn = 1e6+10;
int dp[3][maxn],n,head[maxn],tot,m,id[maxn];
int que1[maxn],que2[maxn],top1,rear1,top2,rear2;
struct Node
{
    int v,nxt,w;
}cur[maxn<<2];
void Init()
{
    memset(head,-1,sizeof(head));
    tot = 0;
}
void Add(int x,int y,int z)
{
    cur[tot].v = y;
    cur[tot].nxt = head[x];
    cur[tot].w = z;
    head[x] = tot++;
}
void dfs1(int rt,int fa)
{
    for(int i=head[rt];i!=-1;i=cur[i].nxt)
    {
        int v = cur[i].v;
        if(fa==v) continue;
        dfs1(v,rt);
        if(dp[0][rt]<dp[0][v]+cur[i].w)
        {
            dp[1][rt] = dp[0][rt];
            dp[0][rt] = dp[0][v] + cur[i].w;
            id[rt] = v;
        }
        else if(dp[1][rt]<dp[0][v] + cur[i].w)
        {
            dp[1][rt] = dp[0][v] + cur[i].w;
        }
    }
}
void dfs2(int rt,int fa)
{
    for(int i=head[rt];i!=-1;i=cur[i].nxt)
    {
        int v = cur[i].v;
        if(fa==v) continue;
        if(id[rt]==v)
        {
            dp[2][v] = max(dp[2][rt],dp[1][rt]) + cur[i].w;
        }
        else
        {
            dp[2][v] = max(dp[2][rt],dp[0][rt]) + cur[i].w;
        }
        dfs2(v,rt);
    }
}
int main(void)
{
    while(~scanf("%d%d",&n,&m))
    {
        Init();
        dp[0][1] = dp[1][1] = dp[2][1] = 0;
        id[1] = 0;
        for(int i=2;i<=n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            Add(x,i,y);
            Add(i,x,y);
            dp[0][i] = dp[1][i] = dp[2][i] = 0;
            id[i] = 0;
        }
        dfs1(1,0);
        dfs2(1,0);
        top1 = rear1 = 1;
        top2 = rear2 = 1;
        int ans = -1;
        for(int i=1,j=1;i<=n;i++)
        {
            dp[0][i] = max(dp[0][i],dp[2][i]);
//注意底下的入队操作。
            while(top1<rear1&&dp[0][ que1[rear1-1] ]<dp[0][i]) rear1--; //mx,保持队列内第一个最大
            que1[rear1++] = i;
            while(top2<rear2&&dp[0][ que2[rear2-1] ]>dp[0][i]) rear2--; //mi,保持队列内第一个最小
            que2[rear2++] = i;
            while(top1<rear1&&top2<rear2&&( dp[0][ que1[top1] ] - dp[0][ que2[top2] ] > m)){
                if(que2[top2] < que1[top1]) j=que2[top2]+1,top2++;
                else j=que1[top1]+1,top1++;
            }
            ans = max(ans,(i-j+1));
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值