• poj3728(lca / tarjan离线)


    题目链接: http://poj.org/problem?id=3728

    题意: 给出一棵带点权值的树, 对于 q 组形如 x, y 的询问, 一个人要从 x 到 y(单向), 他可以在路上任意一点以此点的的权值买一件物品, 并在接下来的路程中任意一点将其以该点的权值卖出, 输出其最大收益, 若不能获益则输出 0 .

    思路: 若不考虑时间复杂度的话对于询问 x, y. 可以先求出 lca(x, y), 然后再 x -> lca -> y 路径上求一下最大收益即可. 然而这样会 tle.

    可以考虑一下化简后面部分.

    对于一组询问 x, y. 最大收益有 3 种情况:

    1. x -> lca 过程中取得最大值

    2. lca -> y 过程中取得最大值

    3. lca -> y 中的最大权值减去 x -> lca 中的最小权值

    为了方便处理这 3 种情况, 分别取

    up 数组, up[x] 为 x -> lca 过程中能取得的最大值

    dow 数组, dow[x] 为 lca -> x 过程中能取得的最大值

    mi 数组, mi[x] 为 x -> lca 过程中最小权值

    mx 数组, mx[x] 为 x -> lca 过程中最大权值

    则第三种情况为: mx[y] - mi[x]

    那么 x, y 的最大收益可以表示为: max(max(up[x], dow[y]), mx[y] - mi[x])

    然而如果对于每一组 x, y 都更新一遍 up, dow, mi, mx 数组的话, 显然也是会 tle 的, 但是我们并没有必要那样做.

    若对于询问 x, y, 我们已经的到了对应的 up, dow, mi, mx 数组, 那么对于 lca 在 lca(x, y) 上面的询问, 即对于 lca 为 lca(x, y) 祖先节点的询问 x1, x2,

    假设 lca(x, y) 在 lca(x1, y1) 左子树中, 我们可以只更新 lca(x, y) 和 y1 到 lca(x1, y1) 对应的数组即可, 而无需更新 x1, y1 到 lca(x1, y1) 对应的数组.

    如此, 我们可以按照询问的 lca 值从树的底层向上更新答案, 只需遍历一次树即可.

    具体操作为; 在 tarjan 递归过程中先处理出所有询问的 lca, 然后在回溯时按 lca 自底向上更新答案即可.

    代码:

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include <string.h>
      4 using namespace std;
      5 
      6 const int MAXN = 5e4 + 10;
      7 struct node{
      8     int v, ip, next;
      9     node(){};
     10     node(int V, int IP, int NEXT) : v(V), ip(IP), next(NEXT){};
     11 }edge1[MAXN << 1], edge2[MAXN << 1], edge3[MAXN << 1];
     12 
     13 int pre[MAXN], a[MAXN], b[MAXN];
     14 int head1[MAXN], head2[MAXN], head3[MAXN], id1, id2, id3;
     15 int vis[MAXN], up[MAXN], dow[MAXN], mi[MAXN], mx[MAXN], sol[MAXN];
     16 
     17 void init(void){
     18     memset(vis, 0, sizeof(vis));
     19     memset(head1, -1, sizeof(head1));
     20     memset(head2, -1, sizeof(head2));
     21     memset(head3, -1, sizeof(head3));
     22     id1 = id2 = id3 = 0;
     23 }
     24 
     25 void addedge1(int u, int v, int ip){
     26     edge1[id1] = node(v, ip, head1[u]);
     27     head1[u] = id1++;
     28 }
     29 
     30 void addedge2(int u, int v, int ip){
     31     edge2[id2] = node(v, ip, head2[u]);
     32     head2[u] = id2++;
     33 }
     34 
     35 void addedge3(int u, int v, int ip){
     36     edge3[id3] = node(v, ip, head3[u]);
     37     head3[u] = id3++;
     38 }
     39 
     40 int find(int v){//压缩路径并处理路径上的 up, dow, mi, mx 数组
     41     if(v == pre[v]) return v;//到叶子节点或者下面的lca时停止递归
     42     int fa = pre[v];
     43     pre[v] = find(pre[v]);
     44     up[v] = max(max(up[v], up[fa]), mx[fa] - mi[v]);//以 v -> lca 为入边
     45     dow[v] = max(max(dow[v], dow[fa]), mx[v] - mi[fa]);//以 lca -> v 为出边
     46     mi[v] = min(mi[v], mi[fa]);
     47     mx[v] = max(mx[v], mx[fa]);
     48     return pre[v];
     49 }
     50 
     51 void tarjan(int u){
     52     vis[u] = 1;
     53     pre[u] = u;
     54     for(int i = head2[u]; i != -1; i = edge2[i].next){
     55         int v = edge2[i].v;
     56         int ip = edge2[i].ip;
     57         if(vis[v]){
     58             int f = find(v);
     59             addedge3(f, v, ip);//记录以 f 为 lca 的询问
     60         }
     61     }
     62     for(int i = head1[u]; i != -1; i = edge1[i].next){
     63         int v = edge1[i].v;
     64         if(!vis[v]){
     65             tarjan(v);
     66             pre[v] = u;
     67         }
     68     }
     69     for(int i = head3[u]; i != -1; i = edge3[i].next){//回溯时计算以 u 为 lca 的询问
     70         int ip = edge3[i].ip;
     71         find(a[ip]);
     72         find(b[ip]);
     73         sol[ip] = max(max(up[a[ip]], dow[b[ip]]), mx[b[ip]] - mi[a[ip]]);
     74     }
     75 }
     76 
     77 int main(void){
     78     int n, x, y, q;
     79     while(~scanf("%d", &n)){
     80         init();
     81         for(int i = 1; i <= n; i++){
     82             scanf("%d", &x);
     83             up[i] = dow[i] = 0;
     84             mi[i] = mx[i] = x;
     85         }
     86         for(int i = 1; i < n; i++){
     87             scanf("%d%d", &x, &y);
     88             addedge1(x, y, i);
     89             addedge1(y, x, i);
     90         }
     91         scanf("%d", &q);
     92         for(int i = 0; i < q; i++){
     93             scanf("%d%d", &x, &y);
     94             a[i] = x;
     95             b[i] = y;
     96             addedge2(x, y, i);
     97             addedge2(y, x, i);
     98         }
     99         tarjan(1);
    100         for(int i = 0; i < q; i++){
    101             printf("%d
    ", sol[i]);
    102         }
    103     }
    104     return 0;
    105 }
    View Code
  • 相关阅读:
    SpringBoot整合RabbitMQ
    RabbitMQ消息确认机制
    RabbitMQ六种队列模式-简单队列模式
    RabbitMQ六种队列模式-工作队列模式
    RabbitMQ六种队列模式-发布订阅模式
    RabbitMQ六种队列模式-路由模式
    RabbitMQ六种队列模式-主题模式
    RabbitMQ简单介绍+Windows环境安装
    SpringBoot整合ActiveMQ发送邮件
    下载缓慢文件记录,持续更新[2019-10-29]
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7222057.html
Copyright © 2020-2023  润新知