zoukankan      html  css  js  c++  java
  • 【Cf #292 D】Drazil and Morning Exercise(树的直径,树上差分)

    有一个经典的问题存在于这个子问题里,就是求出每个点到其他点的最远距离。

    这个问题和树的直径有很大的关系,因为事实上距离每个点最远的点一定是直径的两个端点。所以我们可以很容易地进行$3$遍$Dfs$就可以算出这个了,并假设它为$d$。

    我们考虑把$d$最小的点设为根,把原树变成一棵有根树,一个重要的结论就是:对于树上每一个节点,它的祖先的$d$一定比它小。

    如果我们枚举了$d$最小的点$x$,那可以选择的点都是在$x$这棵子树内的,并且满足$d_{i} - d_{x} <= L$的$i$。

    显然直接求解不太行,我们考虑每个点对它祖先的贡献比较合理,对于每个点$x$而言,只有距离它超过$L$的点才不会将$x$的贡献计入,我们可以倍增找到最浅的满足条件的祖先,然后在那里打上差分标记,当递归走出该点时就取消$x$的贡献。

    这样我们每个询问就可以$O(nlogn)$做了。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    const int N = 100005, LOG = 19;
    
    int n, m, rt, ans;
    int gr[LOG][N], cnt[N];
    LL d[N], L;
    
    int yun, las[N], to[N << 1], pre[N << 1], wi[N << 1];
    inline void Add(int a, int b, int c) {
      to[++yun] = b; wi[yun] = c; pre[yun] = las[a]; las[a] = yun;
    }
    
    void Dfs(int x, int fat, LL dis) {
      d[x] = max(d[x], dis);
      for (int i = las[x]; i; i = pre[i]) {
        if (to[i] == fat) continue;
        Dfs(to[i], x, dis + wi[i]);
      }
    }
    void Dfs_(int x, int fat) {
      for (int i = 1; i < LOG; ++i) {
        if (gr[i - 1][x]) gr[i][x] = gr[i - 1][gr[i - 1][x]];
      }
      for (int i = las[x]; i; i = pre[i]) {
        if (to[i] == fat) continue;
        gr[0][to[i]] = x;
        Dfs_(to[i], x);
      }
    }
    int Solve(int x, int fat) {
      int num = 1, t = x;
      for (int i = las[x]; i; i = pre[i]) {
        if (to[i] == fat) continue;
        num += Solve(to[i], x);
      }
      num -= cnt[x];
      ans = max(ans, num);
      for (int i = LOG - 1; ~i; --i) {
        if (gr[i][t] && d[x] - d[gr[i][t]] <= L) t = gr[i][t];
      }
      ++cnt[gr[0][t]];
      return num;
    }
    
    int main() {
      scanf("%d", &n);
      for (int i = 1, x, y, z; i < n; ++i) {
        scanf("%d%d%d", &x, &y, &z);
        Add(x, y, z); Add(y, x, z);
      }
      Dfs(1, 0, 0);
      rt = max_element(d + 1, d + 1 + n) - d;
      Dfs(rt, 0, 0);
      rt = max_element(d + 1, d + 1 + n) - d;
      Dfs(rt, 0, 0);
      rt = min_element(d + 1, d + 1 + n) - d;
      Dfs_(rt, 0);
    
      scanf("%d", &m);
      for (; m; --m) {
        scanf("%lld", &L);
        memset(cnt, 0, sizeof cnt);
        ans = 0;
        Solve(rt, 0);
        printf("%d
    ", ans);
      }
      
      return 0;
    }
    View Code

    $igodot$ 技巧&套路:

    • 树上每个点到其他点的最远距离
    • 树上差分的技巧
  • 相关阅读:
    程序员老鸟写sql语句的经验之谈
    工作流系统在OA系统中应用
    安卓手机模拟器的安装
    专注于 web报表, web打印, 自定义web表单, web工作流管理系统 方面的技术
    工作流系统之自定义脚本的实现方式
    12306,是bug呢还是bug呢?
    精雕细琢工作流的状态管理
    eworkflow,eform,ebiao和信息系统的集成过程(for dotnet)
    WPF界面UI设计开发心得
    云计算从基础到应用架构系列索引
  • 原文地址:https://www.cnblogs.com/Dance-Of-Faith/p/9398479.html
Copyright © 2011-2022 走看看