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

原文地址:https://www.cnblogs.com/yousiki/p/6292224.html