zoukankan      html  css  js  c++  java
  • BZOJ 3626: [LNOI2014]LCA

    3626: [LNOI2014]LCA

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 2074  Solved: 828
    [Submit][Status][Discuss]

    Description

    给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
    设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
    有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
    (即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

    Input

    第一行2个整数n q。
    接下来n-1行,分别表示点1到点n-1的父节点编号。
    接下来q行,每行3个整数l r z。

    Output

    输出q行,每行表示一个询问的答案。每个答案对201314取模输出

    Sample Input

    5 2
    0
    0
    1
    1
    1 4 3
    1 4 2

    Sample Output

    8
    5

    HINT

    共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。


    Source

    [Submit][Status][Discuss]

    树链剖分+线段树

    显然,暴力求解的复杂度是无法承受的。
    考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。

     1 #include <bits/stdc++.h>
     2 const int siz = 50005;
     3 int n, m, hd[siz], to[siz], nt[siz], tot;
     4 long long ans[siz], sum[siz << 2], tag[siz << 2];
     5 int sz[siz], fa[siz], sn[siz], tp[siz], ps[siz], cnt;
     6 void dfs1(int u) {
     7     sz[u] = 1;
     8     for (int i = hd[u]; ~i; i = nt[i]) {
     9         fa[to[i]] = u; dfs1(to[i]); sz[u] += sz[to[i]];
    10         if (sz[to[i]] > sz[sn[u]])sn[u] = to[i];
    11     }
    12 }
    13 void dfs2(int u) {
    14     if (sn[fa[u]] == u)tp[u] = tp[fa[u]];
    15     else {
    16         tp[u] = u;
    17         for (int i = u; i; i = sn[i])
    18             ps[i] = ++cnt;
    19     } 
    20     for (int i = hd[u]; ~i; i = nt[i])dfs2(to[i]);
    21 }
    22 struct Q {
    23     int x, y, z, id;
    24     Q(void) {};
    25     Q(int a, int b, int c, int d) :
    26         x(a), y(b), z(c), id(d) {};
    27 }q[siz << 1]; int qt;
    28 bool operator < (const Q &a, const Q &b) {
    29     return a.x < b.x;
    30 }
    31 void add(int t, int l, int r, long long k) {
    32     tag[t] += k;
    33     sum[t] += k * (r - l + 1);
    34 }
    35 void pushdown(int t, int l, int r) {
    36     if (!tag[t])return;
    37     int mid = (l + r) >> 1;
    38     add(t << 1, l, mid, tag[t]);
    39     add(t << 1 | 1, mid + 1, r, tag[t]);
    40     tag[t] = 0LL;
    41 }
    42 void add(int t, int l, int r, int x, int y) {
    43     if (l == x && y == r)add(t, l, r, 1);
    44     else {
    45         pushdown(t, l, r);
    46         int mid = (l + r) >> 1;
    47         if (y <= mid)add(t << 1, l, mid, x, y);
    48         else if (x > mid)add(t << 1 | 1, mid + 1, r, x, y);
    49         else add(t << 1, l, mid, x, mid), add(t << 1 | 1, mid + 1, r, mid + 1, y);
    50         sum[t] = sum[t << 1] + sum[t << 1 | 1];
    51     }
    52 }
    53 long long ask(int t, int l, int r, int x, int y) {
    54     if (l == x && y == r)return sum[t];
    55     pushdown(t, l, r); 
    56     int mid = (l + r) >> 1;
    57     if (y <= mid)return ask(t << 1, l, mid, x, y);
    58     if (x > mid)return ask(t << 1 | 1, mid + 1, r, x, y);
    59     return ask(t << 1, l, mid, x, mid) + ask(t << 1 | 1, mid + 1, r, mid + 1, y);
    60 }
    61 void insert(int u) {
    62     while (u) {
    63         int v = tp[u];
    64         add(1, 1, n, ps[v], ps[u]);
    65         u = fa[v];
    66     }
    67 }
    68 long long qry(int u) {
    69     long long ret = 0;
    70     while (u) {
    71         int v = tp[u];
    72         ret += ask(1, 1, n, ps[v], ps[u]);
    73         u = fa[v];
    74     }
    75     return ret;
    76 }
    77 signed main(void) {
    78 //    freopen("in", "r", stdin);
    79     scanf("%d%d", &n, &m);
    80     memset(hd, -1, sizeof(hd));
    81     for (int i = 2, f; i <= n; ++i)
    82         scanf("%d", &f), nt[tot] = hd[++f], to[tot] = i, hd[f] = tot++;
    83     dfs1(1); dfs2(1);
    84     for (int i = 1, x, y, z; i <= m; ++i) {
    85         scanf("%d%d%d", &x, &y, &z); ++x, ++y, ++z;
    86         q[qt++] = Q(x - 1, z, -1, i);
    87         q[qt++] = Q(y, z, 1, i);
    88     }
    89     std::sort(q, q + qt);
    90     for (int i = 0, j = 0; i <= n; ++i) {
    91         insert(i);
    92         for (; q[j].x == i; ++j)
    93             ans[q[j].id] += q[j].z * qry(q[j].y);
    94     }
    95     for (int i = 1; i <= m; ++i)printf("%lld
    ", ans[i] % 201314);
    96 }

    @Author: YouSiki

  • 相关阅读:
    Linux网络设置
    用户权限 文件或目录权限
    开始写博客了
    php开发中如何判断 是否微信访问
    Linux——安装docker以及docker常用命令
    Java——下划线转驼峰
    前端——JS实现多条件过滤数组
    Linux——通过docker搭建禅道
    免安装版MySQL(windows解压版)安装详细教程以及过程中的问题解决
    数据库——SQL通过某字段的取值范围进行分组汇总
  • 原文地址:https://www.cnblogs.com/yousiki/p/6292224.html
Copyright © 2011-2022 走看看