[BZOJ4012][HNOI2015]开店(动态点分治)
2018-04-01 22:44
465 查看
题目大意就是:一个 nn 个节点的树,边和点都带权。每次询问点权在 [L,R][L,R] 范围内的所有点到 uu 的距离之和,强制在线。
(注意下面所说的「父亲」「子节点」「子树」「祖先」等概念都是在分治树上的)
考虑动态点分治时对于每个点 uu ,储存 uu 的子树内所有点到 uu 的父亲的距离。
但这样显然还不能满足我们的要求,因为我们每次查询的是点权在 [L,R][L,R] 范围内的所有点 。
因此,还需要对于 uu 的子树内的所有点,按照点权从小到大排序,并维护排序后这些点到 uu 的父亲的距离的前缀和。
这样,如果只考虑求 uu 的子树内点权在 [L,R][L,R] 范围内的所有点到 uu 的距离之和,那么就可以枚举 uu 的子节点 vv (所有点的度数 ≤3≤3),利用二分查找 + 前缀和来统计答案了。
扩展到整棵树,又要怎么做呢?
考虑枚举 uu 的祖先。
设 ss 是 uu 的一个祖先, tt 是 ss 的一个子节点且是 uu 的祖先。那么这时候,我们需要求出 ss 的子树内的所有点(不包括 tt 的子树内的点)到 uu 的距离之和。
考虑把上面要求出的东西拆成两个:
(1) ss 的子树内的所有点( tt 的子树除外)到 ss 的距离,这个也可以枚举 ss 的子节点后利用二分查找 + 前缀和来统计。
(2) (size[s,L,R]−size[t,L,R])×dist(s,u)(size[s,L,R]−size[t,L,R])×dist(s,u)
上式中:
size[x,L,R]size[x,L,R] 表示点 xx 的子树内点权在 [L,R][L,R] 内的点数。
dist(x,y)dist(x,y) 表示原树上 xx 和 yy 两点距离。
这个式子也比较好理解, ss的子树内的所有合法点( tt 的子树除外)的个数就是括号里的式子。求两点之间的距离,最好使用 dfs + RMQ 来求 LCA 。
设 n,qn,q 同阶的情况下,复杂度 O(nlog2n)O(nlog2n) 。
代码:
(注意下面所说的「父亲」「子节点」「子树」「祖先」等概念都是在分治树上的)
考虑动态点分治时对于每个点 uu ,储存 uu 的子树内所有点到 uu 的父亲的距离。
但这样显然还不能满足我们的要求,因为我们每次查询的是点权在 [L,R][L,R] 范围内的所有点 。
因此,还需要对于 uu 的子树内的所有点,按照点权从小到大排序,并维护排序后这些点到 uu 的父亲的距离的前缀和。
这样,如果只考虑求 uu 的子树内点权在 [L,R][L,R] 范围内的所有点到 uu 的距离之和,那么就可以枚举 uu 的子节点 vv (所有点的度数 ≤3≤3),利用二分查找 + 前缀和来统计答案了。
扩展到整棵树,又要怎么做呢?
考虑枚举 uu 的祖先。
设 ss 是 uu 的一个祖先, tt 是 ss 的一个子节点且是 uu 的祖先。那么这时候,我们需要求出 ss 的子树内的所有点(不包括 tt 的子树内的点)到 uu 的距离之和。
考虑把上面要求出的东西拆成两个:
(1) ss 的子树内的所有点( tt 的子树除外)到 ss 的距离,这个也可以枚举 ss 的子节点后利用二分查找 + 前缀和来统计。
(2) (size[s,L,R]−size[t,L,R])×dist(s,u)(size[s,L,R]−size[t,L,R])×dist(s,u)
上式中:
size[x,L,R]size[x,L,R] 表示点 xx 的子树内点权在 [L,R][L,R] 内的点数。
dist(x,y)dist(x,y) 表示原树上 xx 和 yy 两点距离。
这个式子也比较好理解, ss的子树内的所有合法点( tt 的子树除外)的个数就是括号里的式子。求两点之间的距离,最好使用 dfs + RMQ 来求 LCA 。
设 n,qn,q 同阶的情况下,复杂度 O(nlog2n)O(nlog2n) 。
代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define For(i, a, b) for (i = a; i <= b; i++) #define Edge(u) for (int e = adj[u], v; e; e = nxt[e]) #define Edge2(u) for (int e = adj2[u], v; e; e = nxt2[e]) using namespace std; inline int read() { int res = 0; bool bo = 0; char c; while (((c = getchar()) < '0' || c > '9') && c != '-'); if (c == '-') bo = 1; else res = c - 48; while ((c = getchar()) >= '0' && c <= '9') res = (res << 3) + (res << 1) + (c - 48); return bo ? ~res + 1 : res; } typedef long long ll; const int N = 15e4 + 5, M = 3e5 + 5, LogN = 22, NLogN = 6e6 + 5; int n, q, A, X , ecnt, nxt[M], adj , go[M], val[M], m, o[M], dep , RMQ[M][LogN], Log , fir , dis , sze , maxs , G, tot, _d , _p , Root, fa , ecnt2, nxt2 , adj2 , go2 , T, le , ri , ag[NLogN]; ll ans, sum[NLogN]; bool vis ; void add_edge(int u, int v, int w) { nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w; nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w; } void add_edge2(int u, int v) { nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v; } void dfs(int u, int fu) { dep[u] = dep[fu] + 1; o[fir[u] = ++m] = u; Edge(u) { if ((v = go[e]) == fu) continue; dis[v] = dis[u] + val[e]; dfs(v, u); o[++m] = u; } } void initRMQ() { int i, j; For (i, 1, m) RMQ[i][0] = o[i]; For (j, 1, 20) For (i, 1, m - (1 << j) + 1) { int x = RMQ[i][j - 1], y = RMQ[i + (1 << j - 1)][j - 1]; RMQ[i][j] = dep[x] < dep[y] ? x : y; } } int dist(int u, int v) { int l = fir[u], r = fir[v]; if (l > r) swap(l, r); int z = Log[r - l + 1], x = RMQ[l][z], y = RMQ[r - (1 << z) + 1][z]; return dis[u] + dis[v] - (dis[dep[x] < dep[y] ? x : y] << 1); } void dfs1(int u, int fu) { sze[u] = 1; maxs[u] = 0; Edge(u) { if ((v = go[e]) == fu || vis[v]) continue; dfs1(v, u); sze[u] += sze[v]; maxs[u] = max(maxs[u], sze[v]); } } void dfs2(int r, int u, int fu) { maxs[u] = max(maxs[u], sze[r] - sze[u]); if (maxs[u] < maxs[G]) G = u; Edge(u) if ((v = go[e]) != fu && !vis[v]) dfs2(r, v, u); } int calcG(int u) {dfs1(u, 0); G = u; dfs2(u, u, 0);} inline bool comp(const int &u, const int &v) {return X[u] < X[v];} void dfs3(int rt, int u, int fu) { _d[u] = dist(rt, u); _p[++tot] = u; Edge(u) if (!vis[v = go[e]] && v != fu) dfs3(rt, v, u); } void iamtooweak(int u, int fu) { calcG(u); int i, t = G; fa[G] = fu; vis[G] = 1; if (!fu) Root = G; else add_edge2(fa[G], G); if (fu) { tot = 0; dfs3(fu, G, 0); ag[le[G] = ++T] = -1; For (i, 1, tot) ag[++T] = _p[i]; ri[G] = T; sort(ag + le[G] + 1, ag + ri[G] + 1, comp); For (i, le[G] + 1, ri[G]) sum[i] = sum[i - 1] + _d[ag[i]], ag[i] = X[ag[i]]; } Edge(G) if ((v = go[e]) != fu && !vis[v]) iamtooweak(v, t); } ll gsum(int u, int l, int r, int &siz) { if (r < ag[le[u] + 1] || l > ag[ri[u]]) return 0; l = (lower_bound(ag + le[u], ag + ri[u] + 1, l) - ag) - 1; r = (upper_bound(ag + le[u], ag + ri[u] + 1, r) - ag) - 1; siz += r - l; return sum[r] - sum[l]; } ll query(int u, int l, int r) { ll res = 0; int o = 0; Edge2(u) res += gsum(v = go2[e], l, r, o); int s = u, w = u; u = fa[u]; while (u) { int sz = 0; Edge2(u) if ((v = go2[e]) != w) res += gsum(v, l, r, sz); if (X[u] >= l && X[u] <= r) sz++; res += 1ll * sz * dist(u, s); w = u; u = fa[u]; } return res; } int main() { int i, u, a, b, c, l, r; n = read(); q = read(); A = read(); Log[0] = -1; For (i, 1, n) X[i] = read(); For (i, 1, n << 1) Log[i] = Log[i >> 1] + 1; For (i, 1, n - 1) a = read(), b = read(), c = read(), add_edge(a, b, c); X[0] = -1; dfs(1, 0); initRMQ(); iamtooweak(1, 0); while (q--) { u = read(); a = read(); b = read(); l = min((a + ans) % A, (b + ans) % A); r = max((a + ans) % A, (b + ans) % A); printf("%lld\n", ans = query(u, l, r)); } return 0; }
相关文章推荐
- bzoj4012 [HNOI2015]开店(动态点分治+二分+STL/树链剖分+主席树)
- BZOJ4012 [HNOI2015]开店 【动态点分治 + splay】
- [BZOJ4012][HNOI2015]开店(动态点分治,树链剖分)
- BZOJ 4012 HNOI 2015 开店 动态点分治
- [动态树分治] BZOJ4012 [HNOI2015]开店
- 【动态树分治】【bzoj 4012】: [HNOI2015]开店
- 【bzoj4012】[HNOI2015]开店 动态点分治+STL-vector
- BZOJ 4012 [HNOI2015]开店
- BZOJ 4012 HNOI 2015 开店(shop) 一道简单的点剖题
- bzoj4012: [HNOI2015]开店
- bzoj 4012: [HNOI2015]开店
- 【BZOJ4012】[HNOI2015]开店 动态树分治+二分
- [BZOJ4012][HNOI2015]开店
- 【BZOJ 4012】[HNOI2015]开店
- BZOJ4012 [HNOI2015]开店 (动态点分治)
- BZOJ 4012: [HNOI2015]开店 -- 动态树分治
- BZOJ-4012 开店(动态树分治)
- bzoj 4012: [HNOI2015]开店 (树链剖分+主席树)
- bzoj 4012: [HNOI2015]开店
- BZOJ4012: [HNOI2015]开店 重链剖分 可持久化线段树