题意:给出一个 n个点的树,每条边有长度,每个点有点权。有 q 个询问,每个询问查询点 u 到所有权值在 [l, r] 中的点的距离之和。
强制在线。
和幻想乡战略游戏差不多。
精髓在前缀和的应用上。
如何提取出 [l, r] 的值呢?我们可以用
vector
来维护。只需记下
dis
的前缀和和点权即可。
定义一个 struct :val 年龄;dis 到当前节点的距离; sum 所有在此节点前面的节点和他自己的距离和(排序后)
那么对于询问 [l, r] 可以转换为 [1, r] - [1, l - 1] 。
因为 lower_bound 是查询第一个大于等于要查询的值的位置,查询序列中第一个大于等于 r 的位置时要 lower_bound( , , r +1) - 1 ,就是要查询的位置,查询 l - 1 的时候同理。
BZOJ 4012 [HNOI2015]开店 —— By zyz
复杂度
O((n+q)log2n)
因为树剖和点分的数组名会重,所以数组名变得奇奇 gaygay 的 …
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N = 4e5 + 5, inf = 0x7fffffff;
struct Edge {
ll to, next, w;
}e[N << 1];
struct Point {
ll val, dis, sum;
Point(ll a, ll b, ll c) { val = a; dis = b; sum = c; }
bool operator < (const Point& a) const {
if(val != a.val) return val < a.val;
if(dis != a.dis) return dis < a.dis;
return sum < a.sum;
}
};
ll cnt = 0;
ll head[N], w[N];
void add(ll u, ll v, ll w) {
e[++ cnt].to = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt;
}
ll fa[N], dep[N], hson[N], size[N], dis[N];
void dfs1(ll u, ll f, ll depth) {
fa[u] = f;
dep[u] = depth;
size[u] = 1;
for (int i = head[u]; i; i = e[i].next) {
ll v = e[i].to;
if (v == f) continue;
dis[v] = dis[u] + e[i].w;
dfs1(v, u, depth + 1);
size[u] += size[v];
if (hson[u] == -1 || size[hson[u]] < size[v]) hson[u] = v;
}
}
ll top[N];
void dfs2(ll u, ll tp) {
top[u] = tp;
if (hson[u] == -1) return ;
dfs2(hson[u], tp);
for (int i = head[u]; i; i = e[i].next) {
ll v = e[i].to;
if (v != fa[u] && v != hson[u]) dfs2(v, v);
}
}
ll lca(ll a, ll b) {
while (top[a] != top[b]) {
if (dep[top[a]] > dep[top[b]]) a = fa[top[a]];
else b = fa[top[b]];
}
return (dep[a] < dep[b]) ? a : b;
}
ll getdis(int x, int y) {
return dis[x] + dis[y] - 2 * dis[lca(x, y)];
}
ll sum, rt = 0;
ll son[N], size_df[N], vis[N];
void getrt(ll u, ll f) {
son[u] = 1;
size_df[u] = 0;
for (int i = head[u]; i; i = e[i].next) {
ll v = e[i].to;
if (v == f || vis[v]) continue;
getrt(v, u);
son[u] += son[v];
size_df[u] = max(size_df[u], son[v]);
}
size_df[u] = max(size_df[u], sum - son[u]);
if (size_df[u] < size_df[rt]) rt = u;
}
void get(ll u, ll f) {
son[u] = 1;
size_df[u] = 0;
for (int i = head[u]; i; i = e[i].next) {
ll v = e[i].to;
if (v == f || vis[v]) continue;
getrt(v, u);
son[u] += son[v];
size_df[u] = max(size_df[u], son[v]);
}
size_df[u] = max(size_df[u], sum - son[u]);
if (size_df[u] < size_df[rt]) rt = u;
}
ll fa_df[N];
void solve(ll u, ll f) { // f 是上一个重心
vis[u] = 1; fa_df[u] = f;
for (int i = head[u]; i; i = e[i].next) {
ll v = e[i].to;
if (vis[v]) continue;
get(v, 0);
sum = size_df[0] = son[v];
getrt(v, rt = 0);
solve(rt, u);
}
}
ll n, Q, A;
vector<Point> sub[N], subfa[N];
void vector_init() {
for (int u = 1; u <= n; u ++) {
sub[u].push_back((Point){w[u], 0, 0});
for (int i = u; fa_df[i]; i = fa_df[i]) {
int disnum = getdis(u, fa_df[i]);
sub[fa_df[i]].push_back((Point){w[u], disnum, 0});
subfa[i].push_back((Point){w[u], disnum, 0});
}
}
for (int u = 1; u <= n; u ++) {
sub[u].push_back(Point(-1, 0, 0)); sub[u].push_back(Point(inf, 0, 0));
subfa[u].push_back(Point(-1, 0, 0)); subfa[u].push_back(Point(inf, 0, 0));
sort(sub[u].begin(), sub[u].end());
sort(subfa[u].begin(), subfa[u].end());
for (int i = 1; i < sub[u].size(); i ++) sub[u][i].sum = sub[u][i - 1].sum + sub[u][i].dis;
for (int i = 1; i < subfa[u].size(); i ++) subfa[u][i].sum = subfa[u][i - 1].sum + subfa[u][i].dis;
}
}
ll query(ll u, ll x) {
ll pos = lower_bound(sub[u].begin(), sub[u].end(), Point(x, 0, 0)) - sub[u].begin() - 1;
ll res = sub[u][pos].sum;
for (int i = u; fa_df[i]; i = fa_df[i]) {
ll dist = getdis(u, fa_df[i]);
ll uu = lower_bound(sub[fa_df[i]].begin(), sub[fa_df[i]].end(), Point(x, 0, 0)) - sub[fa_df[i]].begin() - 1;
ll uf = lower_bound(subfa[i].begin(), subfa[i].end(), Point(x, 0, 0)) - subfa[i].begin() - 1;
res += sub[fa_df[i]][uu].sum - subfa[i][uf].sum;
res += dist * (uu - uf);
}
return res;
}
int main() {
ll oldrt;
memset(hson, -1, sizeof(hson));
memset(vis, 0, sizeof(vis));
scanf("%lld%lld%lld", &n, &Q, &A);
for (int i = 1; i <= n; i ++) scanf("%lld", &w[i]);
for (int i = 1; i < n; i ++) {
ll a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
sum = size_df[0] = n;
getrt(1, 0);
oldrt = rt;
dfs1(rt, 0, 0), dfs2(rt, rt);
solve(rt, 0);
vector_init();
ll ans = 0;
for (int i = 1; i <= Q; i ++) {
ll u, a, b;
scanf("%lld%lld%lld", &u, &a, &b);
ll l = min((a + ans) % A, (b + ans) % A), r = max((a + ans) % A, (b + ans) % A);
ans = query(u, r + 1) - query(u, l);
printf("%lld\n", ans);
}
return 0;
}

本文介绍了一种处理树形结构数据的算法,该算法能够在线地解决点权在一定范围内的路径距离求和问题。通过使用前缀和技巧和数据结构优化,实现了高效查询,适用于竞赛编程及相似问题。
513

被折叠的 条评论
为什么被折叠?



