[解题报告]ACM-ICPC Regionals 2011 Asia - Fuzhou C Bob's Race

Abstract

ACM-ICPC Regionals 2011 Asia - Fuzhou C Bob's Race

POJ4003

树形dp 单调队列

 

Body

Source

http://poj.org/problem?id=4003

Description

给定一棵N个节点的树,数列d[i] (1 <= i <= N)表示树中离点i最远的点离点i的距离。现给定Q,求d[i]的最长连续子序列的长度,使得其满足该子序列中最大最小值之差不超过Q。

Solution

这个题属于比较生硬的凑算法的题……问题可以分为两部分:

1. 求数列d[i]。这里用的是非常经典的一种树形dp方法,我以前不知道叫什么,今天据章总和戴牛说叫“上下树”(好猥琐……),长春邀请赛也出了,有空可以写个专题。这种方法在对树的每个节点进行全局统计且要求复杂度为O(N)时经常用到。“上下树”的名称大概来源于这种树形dp进行两次dp,第一次自底向上(叶到根),第二次自顶向下(根到叶)。对于此题,考虑将一个点拎出来作树根后,某个点v的最远距离,要么是v向子孙节点走的最远距离,要么是其父节点u不经过(u,v)的最远距离+w(u,v)。可以把边(u,v)看作分界,u在左v在右,则前者是v往右走的最远距离,后者是v往左走的最远距离。

因此,第一次树形dp要求出点u向子孙节点走的最远距离max1[u],向哪个子节点走最远(设为maxv[u])以及向子孙节点走的次远距离max2[u](为何要求接下来说明)。第二次树形dp对于点v,d[v]=max(x, max1[v]),其中x为v的父节点不经过(u,v)的最远距离+w(u,v)根据v是否是maxv[u],x是不一样的,因此可能用到向哪个子节点走最远(设为maxv[u])以及向子孙节点走的次远距离max2[u]。

2. 求d[i]的最长连续子序列的长度。网上很多说用RMQ做,我太弱搞不懂为啥会用到RMQ,我用的是单调队列做的。

显然应考察以d[i]结尾的最长子序列。假设已知以d[i-1]结尾的最长子序列X,为了求d[i]结尾的最长子序列Y,我们还需要知道X集中的最大数,设为d[j]。若|d[i]-d[j]|>Q则Y的起始下标>j且应继续考察d[j]后的最大数d[j']。若|d[i]-d[j']|>Q则Y的起始下标>j'且应继续考察d[j']后的最大数d[j'']等等。类似的还需要知道X集中的最小数d[k],d[k]后最小数d[k'],d[k']后最小数d[k'']等。这样容易得到Y。这种信息要结构化存储,而且加入d[i]后还要O(1)转移,你想到什么,反正我想到的是单调队列。事实上d[j],d[j'],d[j'']...构成的就是一个单调增队列。

顺便,那次比赛留下了无可挽回的遗憾,家乡真是我永远的伤心之地……

Code

注意,STL的deque会被POJ猥琐卡常数,必须手写。

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

int N, M, Q, ans, st;
vector<int> adj[50050], w[50050];
int max1[50050], max2[50050], maxv[50050];
int d[50050];
int inc[50050],ih, it, dec[50050], dh, dt;

void init()
{
    for (int i = 1; i <= N; ++i)
    {
        adj[i].clear();
        w[i].clear();
    }
}

inline void ae(int u, int v, int d)
{
    adj[u].push_back(v);
    w[u].push_back(d);
    adj[v].push_back(u);
    w[v].push_back(d);
}

void dfs1(int f, int u)
{
    max1[u] = max2[u] = maxv[u] = 0;
    int v, tmp;
    for (int i = 0; i < adj[u].size(); ++i)
    {
        v = adj[u][i];
        if (v==f) continue;
        dfs1(u, v);
        tmp = max1[v]+w[u][i];
        if (tmp >= max1[u])
        {
            max2[u] = max1[u];
            max1[u] = tmp;
            maxv[u] = v;
        }
        else if (tmp > max2[u])
            max2[u] = tmp;
    }
}


void dfs2(int f, int u, int l)
{
    d[u] = max(l, max1[u]);
    int v, tmp;
    for (int i = 0; i < adj[u].size(); ++i)
    {
        v = adj[u][i];
        if (v==f) continue;
        if (v != maxv[u]) dfs2(u, v, d[u]+w[u][i]);
        else dfs2(u, v, max(max2[u], l)+w[u][i]);
    }
}

int main()
{
    int i, j, k, u, v;
    while (scanf("%d%d", &N, &M), N||M)
    {
        init();
        for (i = 1; i < N; ++i)
        {
            scanf("%d%d%d", &u, &v, &k);
            ae(u, v, k);
        }
        dfs1(0, 1);
        dfs2(0, 1, 0);
        while (M--)
        {
            scanf("%d", &Q);
            ans = 1;
            ih = it = dh = dt = 0;
            inc[it++] = 1;
            dec[dt++] = 1;
            st = 0;
            for (i = 2; i <= N; ++i)
            {
                j = k = st;
                while (ih!=it && abs(d[i]-d[inc[ih]])>Q)
                    j = inc[ih++];
                while (dh!=dt && abs(d[i]-d[dec[dh]])>Q)
                    k = dec[dh++];
                st = max(j, k);
                ans = max(ans, i-st);
                while (ih!=it && d[i]<=d[inc[it-1]]) it--;
                inc[it++] = i;
                while (dh!=dt && d[i]>=d[dec[dt-1]]) dt--;
                dec[dt++] = i;
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}
posted @ 2012-04-28 01:21  杂鱼  阅读(347)  评论(0编辑  收藏  举报