【HNOI 2015 开店】【动态点分治 + 前缀和】

题意:给出一个 n个点的树,每条边有长度,每个点有点权。有 q 个询问,每个询问查询点 u 到所有权值在 [l, r] 中的点的距离之和。
强制在线。

和幻想乡战略游戏差不多。
精髓在前缀和的应用上。

如何提取出 [l, r] 的值呢?我们可以用 vector 来维护。只需记下 dis 的前缀和和点权即可。
定义一个 structval 年龄;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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值