解题思路:树形DP + 线段树。虽然数据量很吓人,但经过分析发现这题就是几个算法的聚合体,没有特别难。
1、先用树形dp思想把每个点到其他某点的最远距离求出来。具体做法是先把树变成有根树,深搜一遍找出每个节点到叶子的最长距离,这样算出来的是向下的最远距离,但还有一个向上即向父亲的那条分支的距离未纳进来计算。之后,再搜一遍把向上的那条分支也算进来,深搜和广搜都可以做。代码写得比较清晰,可以看下代码。这一步其实就是Hdu 2196 Computer,之前做过那题觉得这步还不是很难。
2、上一步得到一个数组,现在要从这个数组里找出连续的一段序列最大值最小值之差小于m,并且长度尽量大。暴力枚举肯定超时,100万*100万的计算量,100s可以算完吧。只能考虑O(N)的算法,因为是找最长的一个序列,我们可以通过维护两个指针来完成这个计算过程,两个指针表示区间的开始和结束,如果这个区间差值=<m,那么区间尾可以下移,如果>m那么区间必须缩小一些,区间头向后移,这样就可以通过不断增大区间头使得这个区间差值<=m.
3、那我们怎么找区间中的最大值最下值呢?用线段树啊,很常规的单点查询线段树,还没更新操作。
测试数据:
10 3
1 1
2 1
3 1
4 1
5 1
6 1
7 1
8 1
9 1
4 6
1 3
1 2
2 1
3 1
1 1
1 3
6 1
1 1
2 1
3 1
1 1
5 1
5 17
3 4
1 1
3 1
1 1
7 13
4 4
1 2
1 6
4 4
3 5
5 4
4 17
1 3
2 1
2 3
5 4
3 1
1 2
1 3
3 4
4 11
1 3
1 1
1 1
7 1
1 6
6 1
5 3
3 4
1 4
3 5
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 1100000 #define INF 2147483647 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct node { int v, len, sum; //sum表示这个分支的距离最大值 node *next; } *head[MAX * 2], tree[MAX * 2]; int n, m, ptr, ans, left, right; int dp[MAX], mmax[MAX * 4], mmin[MAX * 4]; void Initial() { ptr = ans = 0; memset(dp, 0, sizeof (dp)); memset(head, NULL, sizeof (head)); } inline int min(int a,int b) { return (a)<(b)?(a):(b); } inline int max(int a,int b) { return (a)>(b)?(a):(b); } void AddEdge(int a, int b, int c) { tree[ptr].v = b, tree[ptr].len = c, tree[ptr].sum = 0; tree[ptr].next = head[a], head[a] = &tree[ptr++]; } void CountDist(int s, int pa) { //转换为以1为根的树后向下计算到叶子的最大距离 node *p = head[s]; while (p != NULL) { if (p->v != pa) { CountDist(p->v, s); dp[s] = max(dp[s], dp[p->v] + p->len); p->sum = dp[p->v] + p->len; } p = p->next; } } void Tree_DP(int s, int pa) { //更新s到pa分支的最远值,并计算dp[s] node *p = head[pa]; int tpmax = 0, i, j; while (p != NULL) { //计算pa节点除s外的最大分支 if (p->v != s) tpmax = max(tpmax, p->sum); p = p->next; } p = head[s]; while (p != NULL) { //这次是向上更新,计算这个节点到其他分支的最大距离 if (p->v == pa) { p->sum = tpmax + p->len; break; } p = p->next; } p = head[s]; while (p != NULL) { //向下递归,并更新dp[s] dp[s] = max(dp[s], p->sum); if (p->v != pa) Tree_DP(p->v, s); p = p->next; } } void Push_Up(int rt) { mmax[rt] = max(mmax[rt << 1], mmax[rt << 1 | 1]); mmin[rt] = min(mmin[rt << 1], mmin[rt << 1 | 1]); } void Build_Tree(int l, int r, int rt) { if (l == r) { mmax[rt] = mmin[rt] = dp[l]; return; } int m = (l + r) >> 1; Build_Tree(lson); Build_Tree(rson); Push_Up(rt); } int Query_Max(int L, int R, int l, int r, int rt) { // if (L <= l && r <= R) return mmax[rt]; int m = (l + r) >> 1, tp1 = 0; if (L <= m) tp1 = max(tp1, Query_Max(L, R, lson)); if (m + 1 <= R) tp1 = max(tp1, Query_Max(L, R, rson)); return tp1; } int Query_Min(int L, int R, int l, int r, int rt) { if (L <= l && r <= R) return mmin[rt]; int m = (l + r) >> 1, tp1 = INF; if (L <= m) tp1 = min(tp1, Query_Min(L, R, lson)); if (m + 1 <= R) tp1 = min(tp1, Query_Min(L, R, rson)); return tp1; } int main() { int i, j, k, a, b, c; int tpmax, tpmin; while (scanf("%d%d", &n, &m) != EOF) { Initial(); for (i = 2; i <= n; ++i) { scanf("%d%d", &b, &c); AddEdge(i, b, c), AddEdge(b, i, c); } CountDist(1, 0); Tree_DP(1, 0); Build_Tree(1, n, 1); left = 1, right = 1; tpmax = tpmin = dp[1]; while (left <= right && right <= n) { if (tpmax - tpmin <= m) { ans = max(ans, right - left + 1); right++; tpmax = max(tpmax,dp[right]); tpmin = min(tpmin,dp[right]); } else { left++; tpmax = Query_Max(left, right, 1, n, 1); tpmin = Query_Min(left, right, 1, n, 1); } if (n - left < ans) break; } printf("%d\n", ans); } }