POJ - 3162 Walking Race 【树上最远距离 + 线段树处理区间最值 + 尺取法 】

传送门
//一棵树, wc第i天从第i个节点开始跑, 记录一个最大值., 然后可以获得n个最大值, 然后从中选取连续的尽可能长的天数使得其中的最大值 - 最小值 <= m 问这个最大的天数是多少.

//思路: 每个点的最大值可由树的直径求出, 然后问题转变为给你一串数字, 从中选取最长的区间, 使得其中的最大最小值之差<=m, 所以处理区间问题当然是用线段树呀, 然后进行尺取, 双指针i,j, 一起往右移, 如果满足就让j++,否则就让i++, 然后一直尺取完即可

AC Code

const int maxn = 1e6+5;
int n,m;
int dis1[maxn],dis2[maxn];
int cnt,head[maxn];
int a[maxn];
struct node
{
    int to,w,next;
}e[maxn*2];
void add(int u,int v,int w)
{
    e[cnt] = (node){v,w,head[u]};
    head[u] = cnt++;
}
int maxlen,st,ed;
void dd(int u,int fa,int len,int flag)
{
    if(flag) dis1[u] = len;
    else dis2[u] = len;
    if(len > maxlen && flag) st = u, maxlen = len;
    for(int i=head[u] ; ~i ; i=e[i].next){
        int to = e[i].to;
        if(to == fa) continue;
        dd(to,u,len+e[i].w,flag);
    }
}
struct Tree
{
    int tl,tr;
    int minn,maxx;
}tree[maxn<<2];
int mi,mx;
void pushup(int id)
{
    tree[id].minn = min(tree[id<<1].minn , tree[id<<1|1].minn);
    tree[id].maxx = max(tree[id<<1].maxx , tree[id<<1|1].maxx);
}
void build(int l,int r,int id)
{
    tree[id].tl = l, tree[id].tr = r;
    if(l == r){
        tree[id].maxx = tree[id].minn = a[l];
        //cout << a[l] << endl;
        return ;
    }
    int mid = (r+l)>>1;
    build(l,mid,id<<1);
    build(mid+1,r,id<<1|1);
    pushup(id);
}
void query(int ql,int qr,int id)
{
    int l = tree[id].tl, r = tree[id].tr;
    if(ql <= l && qr >= r ){
        mi = min(mi,tree[id].minn);
        mx = max(mx,tree[id].maxx);
        return ;
    }
    int mid = (l + r) >> 1;
    if(ql <= mid) query(ql,qr,id<<1);
    if(qr > mid) query(ql,qr,id<<1|1);
}
void solve()
{
    while(~scanf("%d%d",&n,&m)){
        cnt = 0; Fill(head,-1);
        for(int i=2;i<=n;i++){
            int u,w;
            scanf("%d%d",&u,&w);
            add(u,i,w); add(i,u,w);
        }
        Fill(dis1,0); Fill(dis2,0);
        maxlen = -1;
        dd(1,-1,0,1);
        ed = st;
        maxlen = -1;
        dd(st,-1,0,1);
        swap(st,ed);
        dd(ed,-1,0,0);
        for(int i=1;i<=n;i++){
            a[i] = max(dis1[i],dis2[i]);
        }
        build(1,n,1);
        int ans = 0;
        int i=1,j=2;
        while(i<=j && j<=n){
            mi = inf,mx = 0;
            query(i,j,1);
            if(mx - mi <= m){
                ans = max(ans,j-i+1);
                j++;
            }
            else i++;
            if(n - i + 1 <= ans) break;
        }
        printf("%d\n",ans);
    }
}

你可能感兴趣的:(树的直径,线段树/RMQ/扫描线,尺取法/双指针)