【树链剖分 差分】bzoj3626: [LNOI2014]LCA
把LCA深度转化的那一步还是挺妙的。之后就是差分加大力数据结构了。
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。
题目分析
我的做法&图片分析
由于树的标号是随便标的,所以这里的$[l,r]$区间操作在树上并没有什么大的特殊意义。
注意到如果$z$是给定的话,那么由于答案可减,我们可以对于序列来分块。
当然分块只是一种想法,再来挖掘一下$z$给定情况的性质。
这里求4和7的LCA。自然,LCA就是两点到根节点的第一个公共节点。不过这幅图可能还没看出什么。
这幅图里7号节点是z,4,6,8节点是与z节点查询的节点。我们把与z节点查询的节点都沿路径向根方向+1。
这样只需要查询z节点到根节点的路径权值和就能算出所有LCA的深度之和了。
注意到这样处理之后,预处理的信息对于任意的z都适用了。
至于题目要求的“根节点深度为1”,正好可以让我们把边权化为点权,做起来也更加方便。
更理论化的题解
在黄学长博客上看到一篇更加严谨的题解:
显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 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 MO = 201314; 3 const int maxn = 50035; 4 const int maxm = 50035; 5 6 struct node 7 { 8 int fa,top,son,tot; 9 }a[maxn]; 10 struct QRs 11 { 12 int x,id,opt; 13 QRs(int a=0, int b=0, int c=0):x(a),id(b),opt(c) {} 14 bool operator < (QRs a) const 15 { 16 return x < a.x; 17 } 18 }q[maxn<<1]; 19 int chain[maxn],chTot; 20 int f[maxn<<2],add[maxn<<2]; 21 int edges[maxm],nxt[maxm],head[maxn],edgeTot; 22 int ansAdd[maxn],ansDel[maxn],qr[maxn]; 23 int n,m,tot; 24 25 int read() 26 { 27 char ch = getchar(); 28 int num = 0; 29 bool fl = 0; 30 for (; !isdigit(ch); ch = getchar()) 31 if (ch=='-') fl = 1; 32 for (; isdigit(ch); ch = getchar()) 33 num = (num<<1)+(num<<3)+ch-48; 34 if (fl) num = -num; 35 return num; 36 } 37 void addedge(int u, int v) 38 { 39 edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot; 40 } 41 void dfs1(int x, int fa) 42 { 43 a[x].fa = fa, a[x].son = -1, a[x].tot = 1; 44 for (int i=head[x]; i!=-1; i=nxt[i]) 45 { 46 int v = edges[i]; 47 dfs1(v, x), a[x].tot += a[v].tot; 48 if (a[x].son==-1||a[a[x].son].tot < a[v].tot) 49 a[x].son = v; 50 } 51 } 52 void dfs2(int x, int top) 53 { 54 chain[x] = ++chTot, a[x].top = top; 55 if (a[x].son==-1) return; 56 dfs2(a[x].son, top); 57 for (int i=head[x]; i!=-1; i=nxt[i]) 58 if (edges[i]!=a[x].son) 59 dfs2(edges[i], edges[i]); 60 } 61 void pushup(int x) 62 { 63 f[x] = (f[x<<1]+f[x<<1|1])%MO; 64 } 65 void pushdown(int x, int l, int r) 66 { 67 if (add[x]){ 68 add[x<<1] += add[x], add[x<<1|1] += add[x]; 69 f[x<<1] += l*add[x], f[x<<1|1] += r*add[x]; 70 f[x<<1] %= MO, f[x<<1|1] %= MO; 71 add[x] = 0; 72 } 73 } 74 void update(int rt, int L, int R, int l, int r) 75 { 76 if (L <= l&&r <= R){ 77 add[rt]++, f[rt] = (f[rt]+r-l+1)%MO; 78 return; 79 } 80 int mid = (l+r)>>1; 81 pushdown(rt, mid-l+1, r-mid); 82 if (L <= mid) update(rt<<1, L, R, l, mid); 83 if (R > mid) update(rt<<1|1, L, R, mid+1, r); 84 pushup(rt); 85 } 86 void updateChain(int x, int y) 87 { 88 while (a[x].top!=a[y].top) 89 { 90 update(1, chain[a[x].top], chain[x], 1, n); 91 x = a[a[x].top].fa; 92 } 93 update(1, chain[y], chain[x], 1, n); 94 } 95 int query(int rt, int L, int R, int l, int r) 96 { 97 if (L <= l&&r <= R) return f[rt]; 98 int mid = (l+r)>>1, ret = 0; 99 pushdown(rt, mid-l+1, r-mid); 100 if (L <= mid) ret += query(rt<<1, L, R, l, mid); 101 if (R > mid) ret += query(rt<<1|1, L, R, mid+1, r); 102 return ret%MO; 103 } 104 int queryChain(int x, int y) 105 { 106 int ret = 0; 107 while (a[x].top!=a[y].top) 108 { 109 ret += query(1, chain[a[x].top], chain[x], 1, n); 110 x = a[a[x].top].fa; 111 } 112 ret += query(1, chain[y], chain[x], 1, n); 113 return ret%MO; 114 } 115 int main() 116 { 117 memset(head, -1, sizeof head); 118 n = read(), m = read(); 119 for (int i=1; i<n; i++) addedge(read(), i); 120 for (int i=1; i<=m; i++) 121 { 122 q[++tot] = QRs(read()-1, i, 0), q[++tot] = QRs(read(), i, 1); 123 qr[i] = read(); 124 } 125 std::sort(q+1, q+tot+1); 126 dfs1(0, -1); 127 dfs2(0, 0); 128 int now = -1; 129 for (int i=1; i<=tot; i++) 130 { 131 while (now < q[i].x) 132 updateChain(++now, 0); 133 int id = q[i].id; 134 if (q[i].opt) 135 ansAdd[id] = queryChain(qr[id], 0); 136 else ansDel[id] = queryChain(qr[id], 0); 137 } 138 for (int i=1; i<=m; i++) 139 printf("%d\n",(ansAdd[i]-ansDel[i]+MO)%MO); 140 return 0; 141 }
END