BZOJ 3626: [LNOI2014]LCA
3626: [LNOI2014]LCA
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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
0
0
1
1
1 4 3
1 4 2
Sample Output
5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
Source
树链剖分+线段树
显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 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\n", ans[i] % 201314); 96 }
@Author: YouSiki