迟到的AC——2011年福州赛C题(hdu 4123)

本文分享了作者最后一次参加ACM竞赛的备赛感悟,并详细解析了一道结合树状动态规划与范围最大查询(RMQ)的题目,包括双DFS算法实现与区间查询优化技巧。
    记得去年每场亚洲赛都以惨败告终,今年是自己最后一次参加ACM了。
    其实自己挺喜欢ACM这个比赛的,只是自己没能达到大牛级别,根本不能靠ACM的成绩找工作,所以,只能早早放弃ACM,但其实感觉挺对不起韩boss的,所以,我试着想将cl123的实力提上去,这样,以后走的也安心点。
最后一个暑假了,要好好加油,争取明年亚洲赛拿个铜吧,银什么的也不期望,个人认为自己实力还达不到拿银的程度,如果真让自己拿了,含金量肯定不高。。。
    现在已经被hdu的人虐爆了,估计想超上他们概率已经很低了,除非队友大爆发(主要是肯下功夫练就行),实力瞬间提高(虽然两个人智商都比我高,但是瞬间是不可能的- -||),不过这个除非没可能,一个无心ACM,一个天天dota,最后一次ACM区域赛也就没什么好奢望的了。。。。

    此题是树状DP+RMQ,话说,我怎么觉得去年那场福州赛RMQ有用到好多,其实B题应该也是能用RMQ去AC的,可能用单调队列比较方便,RMQ有点小题大做了,但是RMQ真能AC啊啊啊啊!!!为什么没有解题报告有RMQ 来AC的呢!!!只有discuss中看到有一个人跟我的想法一样。。。。无奈~
网上好多解题报告说这是树状DP,其实也就是两次dfs,看不出来有DP的部分。
步骤如下:
1》先dfs求出每个节点的到其子节点的距离的最大值和次最大值,即记录以该节点X为根节点的子树,X到其子节点最远距离的值和次最远距离的值;
2》第二次dfs,求出该节点X经过父节点所能到达的最远距离为:
1)如果X到其父节点的距离加上X到其子节点的最远距离的和等于父节点的最远距离,那么就使用父节点的次最远距离作为最远距离(至于为什么这样?可以假定如果其父节点的最远距离就是由X节点的最远距离+X节点到父节点距离的和,那么只能使用其次最远距离,如果不是,那么其次最远距离肯定等于最远距离)
2)如果X到其父节点的距离+X到其子节点的最远距离不等于父节点的最远距离,那么就使用父节点的最远距离作为最远距离;
3》取该节点X经过父节点所能到达的最远距离和X节点到子节点最远距离中的最大值作为X所能到达的最远距离。
4》利用RMQ求区间的最值,这里要注意RMQ必须使用for循环的,而不要使用log的,否则会TLE,我第一次提交TLE就是因为用了log的RMQ,当时以为自己想法错了,但无论怎么计算复杂度都没有能超时的地方,后来有看他们说必须使用for循环的RMQ。
5》在枚举区间的时候可以用点小贪心。


#include<stdio.h>
#include<string>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<iostream>
using namespace std;
#define maxs( a , b ) a>b?a:b
#define mins( a , b ) a>b?b:a
#define M 50500

struct Point {
    int maxn, minn;
} pnt[M];

struct node {
    int u, v, next, c;
    int flag;
} edge[M * 2];
int head[M], tot;
bool mark[M];
int l[M];

void Swap(int &x, int &y) {
    int t = x;
    x = y;
    y = t;
}

void _add(int u, int v, int c) {
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].c = c;
    edge[tot].flag = false;
    edge[tot].next = head[u];
    head[u] = tot++;
}

void add(int u, int v, int c) {
    _add(u, v, c);
    _add(v, u, c);
}

void dfs(int x) {
    mark[x] = true;
    pnt[x].maxn = pnt[x].minn = 0;
    for (int i = head[x]; i != -1; i = edge[i].next) {
        if (mark[edge[i].v] == false) {
            edge[i].flag = true;
            dfs(edge[i].v);
            int t = pnt[edge[i].v].maxn + edge[i].c;
            if (t > pnt[x].minn && t <= pnt[x].maxn) {
                pnt[x].minn = t;
            } else if (t > pnt[x].minn && t > pnt[x].maxn) {
                Swap(pnt[x].maxn, pnt[x].minn);
                pnt[x].maxn = t;
            }
        }
    }
}

void dfs1(int x) {
    for (int i = head[x]; i != -1; i = edge[i].next) {
        if (edge[i].flag) {
            int t;
            if (pnt[x].maxn == pnt[edge[i].v].maxn + edge[i].c) {
                t = pnt[x].minn + edge[i].c;
            } else {
                t = pnt[x].maxn + edge[i].c;
            }

            if (t > pnt[edge[i].v].minn && t <= pnt[edge[i].v].maxn) {
                pnt[edge[i].v].minn = t;
            } else if (t > pnt[edge[i].v].minn && t > pnt[edge[i].v].maxn) {
                Swap(pnt[edge[i].v].maxn, pnt[edge[i].v].minn);
                pnt[edge[i].v].maxn = t;
            }
            l[edge[i].v] = pnt[edge[i].v].maxn;
            dfs1(edge[i].v);
        }
    }
}
int st1[20][M], st2[20][M];
int n;

void create_Dpmax() {
    for (int j = 0; j < n; ++j)
        st1[0][j] = l[j];
    for (int i = 0; i + 1 < 20; ++i)
        for (int j = 0; j + (1 << i) < n; ++j)
            st1[i + 1][j] = max(st1[i][j], st1[i][j + (1 << i)]);
}
// [begin, end)

int getmax(int begin, int end) {//max{val[begin],val[begin + 1]......val[end - 1]}
    int k = 0;
    while ((1 << (k + 1)) < end - begin)
        ++k;
    return max(st1[k][begin], st1[k][end - (1 << k)]);
}

void create_Dpmin() {
    for (int j = 0; j < n; ++j)
        st2[0][j] = l[j];
    for (int i = 0; i + 1 < 20; ++i)
        for (int j = 0; j + (1 << i) < n; ++j)
            st2[i + 1][j] = min(st2[i][j], st2[i][j + (1 << i)]);
}
// [begin, end)

int getmin(int begin, int end) {//min{val[begin],val[begin + 1]......val[end - 1]}
    int k = 0;
    while ((1 << (k + 1)) < end - begin)
        ++k;
    return min(st2[k][begin], st2[k][end - (1 << k)]);
}

int Dist(int x, int y) {
    return getmax(x - 1, y) - getmin(x - 1, y);
}

int main() {
    int m, i, j;
    while (scanf("%d%d", &n, &m) != EOF) {
        if (n == 0 && m == 0) {
            break;
        }
        memset(head, -1, sizeof (head));
        tot = 0;
        for (i = 1; i < n; i++) {
            int u, v, c;
            scanf("%d%d%d", &u, &v, &c);
            add(u, v, c);
        }
        memset(mark, false, sizeof (mark));
        dfs(1);
        l[1] = pnt[1].maxn;
        dfs1(1);
        for (i = 0; i < n; i++) {
            l[i] = l[i + 1];
        }
        create_Dpmin();
        create_Dpmax();
        while (m--) {
            int q;
            scanf("%d", &q);
            int ans = 1;
            for (i = 1; i <= n - ans; i++) {
                for (j = i + ans; j <= n;) {
                    if (Dist(i, j) <= q) {
                        j++;
                        if (j - i > ans) {
                            ans = j - i;
                        }
                    } else {
                        break;
                    }
                }
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值