[HNOI 2015]开店
Description
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到
Input
第一行三个用空格分开的数 n、Q和A,表示树的大小、开店的方案个数和妖
Output
对于每个方案,输出一行表示方便值。
Sample Input
0 0 7 2 1 4 7 7 7 9
1 2 270
2 3 217
1 4 326
2 5 361
4 6 116
3 7 38
1 8 800
6 9 210
7 10 278
8 9 8
2 8 0
9 3 1
8 0 8
4 2 7
9 7 3
4 7 0
2 2 7
3 2 1
2 3 4
Sample Output
957
7161
9466
3232
5223
1879
1669
1282
0
HINT
满足 n<=150000,Q<=200000。对于所有数据,满足 A<=10^9
题解
若 $u,v$ 在一棵树上,显然存在 $$dist_{u, v} = dist_{root, u}+dist_{root, v}-2*dist_{root, lca(u, v)}$$
其中 $dist_{u, v}$ 表示树上 $u<->v$ 之间的路径长度。
而对于此题,显然每组询问 $[L, R]$ 的答案,就是(记点权在集合 $[L, R]$ 中的点为点集 $S$ ):
$$ans = \sum_{v \in S} (dist_{root, u}+dist_{root, v}-2*dist_{root, lca(u, v)})$$
$$= |S|*dist_{root, u}+\sum_{v \in S}dist_{root, v}-2*\sum_{v \in S}dist_{root, lca(u, v)}$$
注意到 $|S|*dist_{root, u}$ 可以直接求;我们按每个点的点权排序,显然 $\sum_{v \in S}dist_{root, v}$ 是可以用前缀和预处理出来的。
现在需要解决的问题就是如何求 $\sum_{v \in S}dist_{root, lca(u, v)}$ 。
我们注意到这样一个事情:对于求 $dist_{root, lca(u, v)}$ ,我们可以先将 $root<->u$ 的路径上的边标记;再询问 $root<->v$ 的边,询问到的有标记边的就是 $root<->lca(u, v)$ 上的边。
对于这个方法,我们可以将按点权排序后的点,按序存到主席树中。询问 $[L, R]$ 就是询问 $[1, R]-[1, L-1]$ 。
实现方面,值得注意的是,对于新建的主席树,因为树剖每次会修改可能不止一个区间,所以在这个版本下新建的节点可以修改,我们记录一个数组 $pre_i$ 表示第 $i$ 个版本(及之前)动态开点开的最大的值。新建节点时,若当前节点 $o > pre$ 即 $o$ 是当前版本才新建的,所以可以直接修改。
询问的时候,若有 $lazy$ 标记,我是直接暴力下放的,不知道有没有更好的方法。
1 //It is made by Awson on 2018.1.1 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <cstdio> 9 #include <string> 10 #include <vector> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define LL long long 16 #define Max(a, b) ((a) > (b) ? (a) : (b)) 17 #define Min(a, b) ((a) < (b) ? (a) : (b)) 18 using namespace std; 19 const int N = 150000; 20 const int M = N*40; 21 const int INF = ~0u>>1; 22 23 int n, q, a, u, v, c, x1, x2; 24 struct tt { 25 int to, next, cost; 26 }edge[(N<<1)+5]; 27 int path[N+5], tot; 28 struct value { 29 int x, id; 30 bool operator < (const value &b) const { 31 return x < b.x; 32 } 33 }w[N+5]; 34 int size[N+5], son[N+5], sonc[N+5], fa[N+5], dep[N+5], pos[N+5], top[N+5], cnt; 35 LL val[N+5], dist[N+5], sumdist[N+5]; 36 struct Segment_tree { 37 int root[N+5], pre[N+5], lazy[M+5], ch[M+5][2], pos; 38 LL sum[M+5]; 39 void pushdown(int o, int l, int mid, int r, int last) { 40 if (ch[o][0] <= last) { 41 int ls = ch[o][0]; ch[o][0] = ++pos; 42 lazy[ch[o][0]] = lazy[ls], ch[ch[o][0]][0] = ch[ls][0], ch[ch[o][0]][1] = ch[ls][1], sum[ch[o][0]] = sum[ls]; 43 } 44 if (ch[o][1] <= last) { 45 int rs = ch[o][1]; ch[o][1] = ++pos; 46 lazy[ch[o][1]] = lazy[rs], ch[ch[o][1]][0] = ch[rs][0], ch[ch[o][1]][1] = ch[rs][1], sum[ch[o][1]] = sum[rs]; 47 } 48 sum[ch[o][0]] += (val[mid]-val[l-1])*lazy[o], sum[ch[o][1]] += (val[r]-val[mid])*lazy[o]; 49 lazy[ch[o][0]] += lazy[o], lazy[ch[o][1]] += lazy[o], lazy[o] = 0; 50 } 51 void update(int &o, int l, int r, int a, int b, int last) { 52 if (o <= last) { 53 int rt = o; o = ++pos; 54 lazy[o] = lazy[rt], ch[o][0] = ch[rt][0], ch[o][1] = ch[rt][1], sum[o] = sum[rt]; 55 } 56 if (a <= l && r <= b) { 57 lazy[o] += 1, sum[o] += val[r]-val[l-1]; 58 return; 59 } 60 int mid = (l+r)>>1; 61 if (lazy[o]) pushdown(o, l, mid, r, last); 62 if (mid >= a) update(ch[o][0], l, mid, a, b, last); 63 if (mid < b) update(ch[o][1], mid+1, r, a, b, last); 64 sum[o] = sum[ch[o][0]]+sum[ch[o][1]]; 65 } 66 LL query(int o, int l, int r, int a, int b) { 67 if (a <= l && r <= b) return sum[o]; 68 int mid = (l+r)>>1; LL c1 = 0, c2 = 0; 69 if (lazy[o]) pushdown(o, l, mid, r, INF); 70 if (mid >= a) c1 = query(ch[o][0], l, mid, a, b); 71 if (mid < b) c2 = query(ch[o][1], mid+1, r, a, b); 72 return c1+c2; 73 } 74 }T; 75 76 void add(int u, int v, int c) { 77 edge[++tot].to = v; 78 edge[tot].cost = c; 79 edge[tot].next = path[u]; 80 path[u] = tot; 81 } 82 void dfs1(int u, int father, int depth) { 83 size[u] = 1, fa[u] = father, dep[u] = depth; 84 for (int i = path[u]; i; i = edge[i].next) 85 if (edge[i].to != father) { 86 dfs1(edge[i].to, u, depth+1); 87 size[u] += size[edge[i].to]; 88 if (size[edge[i].to] > size[son[u]]) son[u] = edge[i].to, sonc[u] = edge[i].cost; 89 } 90 } 91 void dfs2(int u, int tp, int cost) { 92 top[u] = tp, pos[u] = ++cnt, val[cnt] = cost; 93 if (son[u]) { 94 dist[son[u]] = dist[u]+sonc[u]; 95 dfs2(son[u], tp, sonc[u]); 96 } 97 for (int i = path[u]; i; i = edge[i].next) 98 if (edge[i].to != fa[u] && edge[i].to != son[u]) { 99 dist[edge[i].to] = dist[u]+edge[i].cost; 100 dfs2(edge[i].to, edge[i].to, edge[i].cost); 101 } 102 } 103 void lca_update(int &o, int x, int last) { 104 while (x) { 105 T.update(o, 1, n, pos[top[x]], pos[x], last); 106 x = fa[top[x]]; 107 } 108 } 109 LL lca_query(int o, int x) { 110 LL ans = 0; 111 while (x) { 112 ans += T.query(o, 1, n, pos[top[x]], pos[x]); 113 x = fa[top[x]]; 114 } 115 return ans; 116 } 117 int lowerbound(int x) { 118 int L = 1, R = n, ans = 1; 119 while (L <= R) { 120 int mid = (L+R)>>1; 121 if (w[mid].x < x) L = mid+1; 122 else R = mid-1, ans = mid; 123 } 124 return ans; 125 } 126 int upperbound(int x) { 127 int L = 1, R = n, ans = 1; 128 while (L <= R) { 129 int mid = (L+R)>>1; 130 if (w[mid].x > x) R = mid-1; 131 else L = mid+1, ans = mid; 132 } 133 return ans; 134 } 135 void work() { 136 scanf("%d%d%d", &n, &q, &a); 137 for (int i = 1; i <= n; i++) scanf("%d", &w[i].x), w[i].id = i; 138 sort(w+1, w+1+n); 139 for (int i = 1; i < n; i++) { 140 scanf("%d%d%d", &u, &v, &c); 141 add(u, v, c); add(v, u, c); 142 } 143 dfs1(1, 0, 1), dfs2(1, 1, 0); 144 for (int i = 1; i <= n; i++) val[i] += val[i-1]; 145 for (int i = 1; i <= n; i++) { 146 T.root[i] = T.root[i-1]; 147 lca_update(T.root[i], w[i].id, T.pre[i-1]); 148 T.pre[i] = T.pos, sumdist[i] = sumdist[i-1]+dist[w[i].id]; 149 } 150 LL ans = 0; 151 while (q--) { 152 scanf("%d%d%d", &u, &x1, &x2); 153 x1 = (x1+ans)%a, x2 = (x2+ans)%a; 154 if (x1 > x2) swap(x1, x2); 155 x1 = lowerbound(x1), x2 = upperbound(x2); 156 LL a1 = lca_query(T.root[x1-1], u), a2 = lca_query(T.root[x2], u); 157 ans = (x2-x1+1)*dist[u]+sumdist[x2]-sumdist[x1-1]-(a2-a1)*2; 158 printf("%lld\n", ans); 159 } 160 } 161 int main() { 162 work(); 163 return 0; 164 }
upd 18.1.8:拿动态点分玩♂下这道题。
网上题解看不懂,自己 YY 了一个做 Fa♂。似乎和题解是一样的...
首先建成点分树后,我们用一个数据结构来维护一下所有点到重心的距离,记点分树上 $o$ 的父亲为 $fa_o$ ,并维护所有点到 $fa_o$ 的距离。
我们可以用 $vector$ 来存下这些数据。对于查询 $[L, R]$ 我们可以二分 $vector$ 。所以我们先不讨论范围问题。
查询时,我们发现,在点分树上爬的时候,我们需要统计除了来的子树外的其他子树中所有节点到当前重心的距离,并且加上这一部分通过重心到询问的点的距离。
如何求这一部分通过重心到询问的点的距离?我们可以在处理上个重心的时候预先减去 $size_x*dist(o, fa_x)$ ,再在处理这个重心时加上 $size_x*dist(x, o)$ 。注意 $x$ 是当前处理的重心,即上述两式中 $x$ 的含义不一样。
如何统计除了来的子树外的其他子树中所有节点到当前重心的距离?也用上面的思想,我们可以先减去以上一个重心为根的子树中所有点到当前处理的的重心的距离和,然后再直接加上以当前处理的重心为根的子树中所有的点到重心的距离。
总时间复杂度就是 $O((n+q)log_2 ^2 n)$ 。
1 //It is made by Awson on 2018.1.8 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <cstdio> 9 #include <string> 10 #include <vector> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define LL long long 16 #define RE register 17 #define lowbit(x) ((x)&(-(x))) 18 #define Max(a, b) ((a) > (b) ? (a) : (b)) 19 #define Min(a, b) ((a) < (b) ? (a) : (b)) 20 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b)) 21 using namespace std; 22 const int N = 150000; 23 const int INF = ~0u>>1; 24 void read(int &x) { 25 char ch; bool flag = 0; 26 for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar()); 27 for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar()); 28 x *= 1-2*flag; 29 } 30 31 int n, q, A, col[N+5], a, b, c; 32 struct tt {int to, next, cost; }edge[(N<<1)+5]; 33 int path[N+5], top; 34 struct arr { 35 LL x, xlca; int id; 36 arr() {} 37 arr(LL _x, LL _xlca, int _id) {x = _x, xlca = _xlca, id = _id; } 38 bool operator < (const arr &b) const {return id < b.id; } 39 }; 40 vector<arr>d[N+5]; 41 int fa[N+5]; 42 LL ans; 43 void add(int u, int v, int c) { 44 edge[++top].to = v; 45 edge[top].next = path[u]; 46 edge[top].cost = c; 47 path[u] = top; 48 } 49 namespace LCA { 50 int bin[25], lim, dep[N+5], fa[N+5][25]; 51 LL dis[N+5]; 52 void dfs(int o, int depth, int father, LL dist) { 53 dis[o] = dist; 54 fa[o][0] = father, dep[o] = depth; 55 for (int i = path[o]; i; i = edge[i].next) 56 if (edge[i].to != father) dfs(edge[i].to, depth+1, o, dist+edge[i].cost); 57 } 58 int query(int x, int y) { 59 if (dep[x] < dep[y]) Swap(x, y); 60 for (int i = lim; i >= 0; i--) if (dep[fa[x][i]] >= dep[y]) x = fa[x][i]; 61 if (x == y) return x; 62 for (int i = lim; i >= 0; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; 63 return fa[x][0]; 64 } 65 LL dist(int x, int y) {return dis[x]+dis[y]-(dis[query(x, y)]<<1); } 66 void main() { 67 lim = log(n)/log(2), bin[0] = 1; for (int i = 1; i <= 20; i++) bin[i] = bin[i-1]<<1; 68 dfs(1, 1, 0, 0); 69 for (int t = 1; t <= lim; t++) for (int i = 1; i <= n; i++) fa[i][t] = fa[fa[i][t-1]][t-1]; 70 } 71 } 72 namespace Point_divide { 73 int size[N+5], mx[N+5], vis[N+5], minsize, root; 74 void get_size(int o, int fa) { 75 size[o] = 1, mx[o] = 0; 76 for (int i = path[o]; i; i = edge[i].next) 77 if (edge[i].to != fa && !vis[edge[i].to]) { 78 get_size(edge[i].to, o); 79 size[o] += size[edge[i].to]; 80 if (size[edge[i].to] > mx[o]) mx[o] = size[edge[i].to]; 81 } 82 } 83 void get_root(int o, int pa, int fa) { 84 mx[o] = Max(mx[o], size[pa]-size[o]); 85 if (mx[o] < minsize) minsize = mx[o], root = o; 86 for (int i = path[o]; i; i = edge[i].next) 87 if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, pa, o); 88 } 89 void get_dist(int o, int rt, int pa, int fa, int dist) { 90 d[rt].push_back(arr(dist, LCA::dist(pa, o), col[o])); 91 for (int i = path[o]; i; i = edge[i].next) 92 if (edge[i].to != fa && !vis[edge[i].to]) get_dist(edge[i].to, rt, pa, o, dist+edge[i].cost); 93 } 94 void solve(int o, int father) { 95 minsize = INF; get_size(o, 0), get_root(o, o, 0); 96 vis[root] = 1, fa[root] = father; d[root].push_back(arr(0, LCA::dist(root, father), col[root])); int rt = root; 97 for (int i = path[root]; i; i = edge[i].next) 98 if (!vis[edge[i].to]) get_dist(edge[i].to, root, father, root, edge[i].cost); 99 d[root].push_back(arr(0, 0, -1)); 100 sort(d[root].begin(), d[root].end()); int size = d[root].size(); 101 for (int i = 1; i < size; i++) d[root][i].x += d[root][i-1].x, d[root][i].xlca += d[root][i-1].xlca; 102 for (int i = path[root]; i; i = edge[i].next) 103 if (!vis[edge[i].to]) solve(edge[i].to, rt); 104 } 105 void main() {solve(1, 0); } 106 } 107 int upperbound(int o, int key) { 108 int L = 0, R = d[o].size()-1, mid, ans = R; 109 while (L <= R) { 110 mid = (L+R)>>1; 111 if (d[o][mid].id <= key) L = mid+1, ans = mid; 112 else R = mid-1; 113 } 114 return ans; 115 } 116 int lowerbound(int o, int key) { 117 int L = 0, R = d[o].size()-1, mid, ans = 0; 118 while (L <= R) { 119 mid = (L+R)>>1; 120 if (d[o][mid].id <= key) L = mid+1, ans = mid; 121 else R = mid-1; 122 } 123 return ans; 124 } 125 void query(int o, int l, int r) { 126 for (int x = o, L, R; x; x = fa[x]) { 127 R = upperbound(x, r), L = lowerbound(x, l-1); 128 ans += (R-L)*LCA::dist(x, o); 129 if (fa[x]) ans -= d[x][R].xlca-d[x][L].xlca+(LL)(R-L)*LCA::dist(fa[x], o); 130 ans += d[x][R].x-d[x][L].x; 131 } 132 } 133 void work() { 134 read(n), read(q), read(A); 135 for (int i = 1; i <= n; i++) read(col[i]); 136 for (int i = 1; i < n; i++) read(a), read(b), read(c), add(a, b, c), add(b, a, c); 137 LCA::main(); Point_divide::main(); 138 while (q--) { 139 read(c), read(a), read(b); a = (ans+a)%A, b = (ans+b)%A; 140 if (a > b) Swap(a, b); ans = 0; 141 query(c, a, b); printf("%lld\n", ans); 142 } 143 } 144 int main() { 145 work(); 146 return 0; 147 }