• UVALive 7148 LRIP(树的分治+STL)(2014 Asia Shanghai Regional Contest)


    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=648&page=show_problem&problem=5160

    There is a tree with N nodes, and every node has a weighted value. A RIP (restricted increasing path)
    is a directed path with all nodes’ weighted values not decreasing and the difference between the max
    weighted value and the min weighted value is not larger than D. Find the length of longest restricted
    increasing path (LRIP).
    A path in a tree is a finite or in finite sequence of edges which connect a sequence of vertices which
    are all distinct from one another. A directed path is again a sequence of edges which connect a sequence
    of vertices, but with the added restriction that the edges all be directed in the same direction.
    Input
    The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts
    with two integers N and D, which indicates the number of nodes in the tree and the restricted value.
    The following line contains N integers, a1, a2, …, ai
    , …, aN , which indicates the i-th node’s weighted
    value. Then N − 1 lines follow, every line contains two integers u, v (1 ≤ u, v ≤ N), which means there
    is a path between u-th node and v-th node.
    Output
    For each test case, output one line containing ‘Case #x: y’, where x is the test case number (starting
    from 1) and y is the length of LRIP of this tree.
    Unofficial clarification: The last N −1 lines for each testcase describe edges, not paths. These edges
    are undirected (i.e. you can make it a directed edge in either direction), and the length of a path is the
    number of nodes on it.
    Limits:
    1 ≤ T ≤ 10
    1 ≤ ai ≤ 10^5
    , 1 ≤ i ≤ N
    1 ≤ N, D ≤ 10^5

    题目大意:给一棵带点权的树,求树上的一条最长不下降路径,使得最大结点和最小结点的差不超过一个给定的D。

    思路:其实直接遍历+启发式合并大概也可以做,但是用树的点分治要容易地多……

    首先,对于每次分治,找到一个分治中心root,寻找“所有”经过root的路径,并把root删去,继续分治。

    那么,如何找到经过root的路径呢?

    假设root的儿子为list(son),依次遍历每个儿子,并维护上升路径的集合,再从所有下降路径中寻找最佳的上升路径。

    维护的集合为上升路径的权值+上升路径的深度(假设根为root)。

    若上升路径的序列中,权值严格递增,深度严格递减(舍弃多余的路径),那么下降路径寻找最佳上升路径的时候,直接二分即可。

    至于维护上面说的集合,可以使用线段树来维护,也可以使用std::map来维护(考验STL水平的时候到了)。

    总复杂度为O(n(logn)^2)。

    PS:什么是多余的上升路径?设权值为val,深度为dep。若val[u]≥val[v]且dep[u]≥dep[v],那么v在任意时刻都不会比u更优,可以舍弃。因为我们要找的是权值大于等于某个值的深度最大的结点。

    代码(0.736S):

      1 #include <cstdio>
      2 #include <iostream>
      3 #include <algorithm>
      4 #include <cstring>
      5 #include <vector>
      6 #include <map>
      7 using namespace std;
      8 typedef long long LL;
      9 
     10 const int MAXV = 100010;
     11 const int MAXE = MAXV * 2;
     12 
     13 int head[MAXV], val[MAXV], ecnt;
     14 int to[MAXE], nxt[MAXE];
     15 int n, D, T, res;
     16 
     17 void init() {
     18     memset(head + 1, -1, n * sizeof(int));
     19     ecnt = 0;
     20 }
     21 
     22 void add_edge(int u, int v) {
     23     to[ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt++;
     24     to[ecnt] = u; nxt[ecnt] = head[v]; head[v] = ecnt++;
     25 }
     26 
     27 int size[MAXV], maxBranch[MAXV];
     28 bool del[MAXV];
     29 vector<int> nodes;
     30 
     31 void dfs_size(int u, int f) {
     32     size[u] = 1;
     33     maxBranch[u] = 0;
     34     for(int p = head[u]; ~p; p = nxt[p]) {
     35         int v = to[p];
     36         if(del[v] || v == f) continue;
     37         dfs_size(v, u);
     38         size[u] += size[v];
     39         maxBranch[u] = max(maxBranch[u], size[v]);
     40     }
     41     nodes.push_back(u);
     42 }
     43 int get_root(int u) {
     44     nodes.clear();
     45     dfs_size(u, -1);
     46     int rt = u;
     47     for(int v : nodes) {
     48         maxBranch[v] = max(maxBranch[v], size[u] - size[v]);
     49         if(maxBranch[v] < maxBranch[rt]) rt = v;
     50     }
     51     return rt;
     52 }
     53 
     54 map<int, int> up;
     55 
     56 void insert(int val, int len) {
     57     auto x = up.lower_bound(val);
     58     if(x != up.end() && x->second >= len) return ;
     59 
     60     auto ed = up.upper_bound(val);
     61     //printf("#debug %d %d
    ", ed->first, ed->second);
     62     auto it = map<int, int>::reverse_iterator(ed);
     63     while(it != up.rend() && it->second <= len) ++it;
     64     up.erase(it.base(), ed);
     65     up[val] = len;
     66 }
     67 
     68 void dfs_up(int u, int f, int dep) {
     69     insert(val[u], dep);
     70     for(int p = head[u]; ~p; p = nxt[p]) {
     71         int v = to[p];
     72         if(!del[v] && v != f && val[v] <= val[u])
     73             dfs_up(v, u, dep + 1);
     74     }
     75 }
     76 
     77 void dfs_down(int u, int f, int dep) {
     78     auto it = up.lower_bound(val[u] - D);
     79     if(it != up.end()) res = max(res, it->second + dep + 1);
     80     for(int p = head[u]; ~p; p = nxt[p]) {
     81         int v = to[p];
     82         if(!del[v] && v != f && val[v] >= val[u])
     83             dfs_down(v, u, dep + 1);
     84     }
     85 }
     86 
     87 void _work(int u, vector<int> &son) {
     88     up.clear();
     89     up[val[u]] = 0;
     90     for(int v : son) {
     91         if(val[v] >= val[u]) dfs_down(v, u, 1);
     92         if(val[v] <= val[u]) dfs_up(v, u, 1);
     93     }
     94 }
     95 void work(int rt) {
     96     vector<int> son;
     97     for(int p = head[rt]; ~p; p = nxt[p])
     98         if(!del[to[p]]) son.push_back(to[p]);
     99 
    100     _work(rt, son);
    101     reverse(son.begin(), son.end());
    102     _work(rt, son);
    103 }
    104 
    105 void solve(int st) {
    106     int u = get_root(st);
    107     work(u);
    108 
    109     del[u] = true;
    110     for(int p = head[u]; ~p; p = nxt[p]) {
    111         int v = to[p];
    112         if(!del[v]) solve(v);
    113     }
    114 }
    115 
    116 int main() {
    117     scanf("%d", &T);
    118     for(int t = 1; t <= T; ++t) {
    119         scanf("%d%d", &n, &D);
    120         init();
    121         for(int i = 1; i <= n; ++i) scanf("%d", &val[i]);
    122         for(int i = 1, u, v; i < n; ++i) {
    123             scanf("%d%d", &u, &v);
    124             add_edge(u, v);
    125         }
    126 
    127         memset(del + 1, 0, n * sizeof(bool));
    128         res = 1;
    129         solve(1);
    130         printf("Case #%d: %d
    ", t, res);
    131     }
    132 }
    View Code

    小插曲:AC过两天居然改数据rejudge了!然而发现机房居然各种不能上网。用爪机看了看代码,大概就少了处理n=1的情况。后来能上网后随手交交就AC了。其他题似乎也rejudge了o(╯□╰)o,还好我平时写完代码都有自己保存,不然就坑爹了。

  • 相关阅读:
    Mysql添加用户与授权
    php导出数据到csv
    mysql导入数据和导出数据
    null在mysql中的不同表现
    乌班图安装Lnmp环境
    php框架路由美化后提示No input file specified
    cookie删除失效问题
    MongoDb安装
    MongoDb简介
    位置索引介绍
  • 原文地址:https://www.cnblogs.com/oyking/p/4499112.html
Copyright © 2020-2023  润新知