POJ 3162 浅谈尺取法区间问题运用及多源树上路径统计

POJ 3162 浅谈尺取法区间问题运用及多源树上路径统计_第1张图片
世界真的很大
NOIP近在咫尺,已是迫在眉睫之时
今天却还是这么水
做完一道上周的遗留问题
这个尺取法为什么叫这个名字我也没搞懂

看题先:

description:

一棵n个节点的树。wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离的差小于M,问怎么取使得天数最多?

input:

The input contains a single test case.
The test case starts with a line containing the integers N (N ≤ 106) and M (M < 109).
Then follow N − 1 lines, each containing two integers fi and di (i = 1, 2, …, N − 1), meaning the check-points i + 1 and fi are connected by a path of length di.

output:

Output one line with only the desired number of days in the longest series.

这个问题分为两个部分
一是求树上每个点在树上的最远点对,二是在给定序列中选取最长的一段使得这一段的最值之差小于等于m
对于第一个问题,同 HDU 2196
现在只需要处理第二个问题了
求出树上最长一段连续区间,其最值之差小于等于m
直接枚举区间n^25不现实,我们需要一个O(n)的做法
这里介绍一个尺取法
所谓尺取法,也不知道具体来说是啥
使用两个指针,如果当前区间的最值之差小于等于m,右指针++,表示可以更新答案
否则左指针++,表示区间需要缩减
大概就是这样
没想到写出来这么短233
维护区间最值可以用ST表,线段树,单调队列等等
鉴于想要练习一下数据结构于是就写了线段树233
表示直到现在还是不会树状数组,也懒得学了

完整代码:

#include
#include
#include
using namespace std;

const int INF=0x3f3f3f3f;

struct node
{
    int mx,mn;
    node *ls,*rs;
    void update()
    {
        mx=max(ls->mx,rs->mx);
        mn=min(ls->mn,rs->mn);
    }
}pool[4000010],*tail=pool,*root;

struct edge
{
    int v,w,last;
}ed[2000010];

int n,m,num=0;
int down[1000010][2],up[1000010],a[1000010],head[1000010],son[1000010];

void init()
{
    num=0,tail=pool;
    memset(down,0,sizeof(down));
    memset(head,0,sizeof(head));
    memset(son,0,sizeof(son));
    memset(up,0,sizeof(up));
    memset(a,0,sizeof(a));
}

node *build(int lf,int rg)
{
    node *nd=++tail;
    if(lf==rg)
    {
        nd->mx=nd->mn=a[lf];
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->ls=build(lf,mid);
    nd->rs=build(mid+1,rg);
    nd->update();
    return nd;
}

void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

void dfs1(int u,int f)
{
    for(int i=head[u];i;i=ed[i].last)
    {
        int v=ed[i].v;
        if(v==f) continue ;
        dfs1(v,u);
        if(down[v][0]+ed[i].w>=down[u][0])
            down[u][1]=down[u][0],down[u][0]=down[v][0]+ed[i].w,son[u]=v;
        else if(down[v][0]+ed[i].w>down[u][1])
            down[u][1]=down[v][0]+ed[i].w;
    }
}

void dfs2(int u,int f)
{
    for(int i=head[u];i;i=ed[i].last)
    {
        int v=ed[i].v;
        if(v==f) continue ;
        if(v==son[u])
            up[v]=max(up[u],down[u][1])+ed[i].w;
        else up[v]=max(up[u],down[u][0])+ed[i].w;
        dfs2(v,u);
    }
}

void query(node *nd,int lf,int rg,int L,int R,int &A,int &B)
{
    if(L<=lf && rg<=R)
    {
        A=nd->mx,B=nd->mn;
        return ;
    }
    int mid=(lf+rg)>>1,a1=0,a2=0,b1=INF,b2=INF;
    if(L<=mid) query(nd->ls,lf,mid,L,R,a1,b1);
    if(R>mid) query(nd->rs,mid+1,rg,L,R,a2,b2);
    A=max(a1,a2),B=min(b1,b2);
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=2;i<=n;i++)
        {
            int v,w;
            scanf("%d%d",&v,&w);
            add(i,v,w),add(v,i,w);
        }
        dfs1(1,1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)
            a[i]=max(down[i][0],up[i]);
        root=build(1,n);
        int i=1,j=1,ans=0;
        while(j<=n && i<=j)
        {
            int mx,mn;
            query(root,1,n,i,j,mx,mn);
            if(mx-mn<=m) ans=max(ans,j-i+1),j++;
            else i++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/

嗯,就是这样

你可能感兴趣的:(poj,DP,线段树,树形DP)