zoukankan      html  css  js  c++  java
  • [HNOI2015]开店

    先考虑不要求区间怎么做,即我们要查询:

    [sumlimits_{i = } ^ n dis_{u, i} = sumlimits_{i = 1} ^ n dep_u + dep_i - 2 imes dep_{lca(u, i)} ]

    前面这一坨 (sumlimits_{i = 1} ^ n dep_u + dep_i = n imes dep_u + sumlimits_{i = 1} ^ n dep_i) 可以很快计算出来,后面那一坨实际上要求的就是 (sumlimits_{i = 1} ^ n dep_{lca(u, i)}),这实际上就是一道原题 [LNOI2014]LCA 我们继续使用这题的套路,先将边权下放到点权,将每个点到根路径上所有点的权值加上这个点的点权,每次查询一个点的 (sumlimits_{i = 1} ^ n dep_{lca(u, i)}) 相当于这个点 (u) 到根节点路径上的权值和,这个我们可以使用树剖解决。再来看有查询限制的情况,我们将每个妖怪按照年龄从小到大排序,以妖怪下标建立主席树,利用主席树的前缀和性质就可以求出有限制的答案了。(其实本题也可当作可持久化树剖模板来看待)

    #include<bits/stdc++.h>
    using namespace std;
    #define ls t[p].l
    #define rs t[p].r
    #define mid (l + r) / 2
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    #define Next(i, u) for(int i = h[u]; i; i = e[i].next)
    typedef long long ll;
    const int N = 150000 + 5;
    const int M = 15000000 + 5;
    struct node{
        int w, p;
        bool operator < (const node &x) const{
            return w == x.w ? p < x.p : w < x.w;
        }
    }a[N];
    struct edge{
        int v, next, w;
    }e[N << 1];
    struct tree{
        int l, r, tag; ll sum;
    }t[M];
    ll ans, dep[N], ton[N];
    int n, m, u, v, w, l, r, x, y, Q, A, tot, cnt, num, last;
    int s[N], h[N], rt[N], fa[N], dfn[N], dis[N], son[N], top[N];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    void add(int u, int v, int w){
        e[++tot].v = v, e[tot].w = w, e[tot].next = h[u], h[u] = tot;
        e[++tot].v = u, e[tot].w = w, e[tot].next = h[v], h[v] = tot;
    }
    void dfs1(int u, int Fa){
        int Max = -1; s[u] = 1, fa[u] = Fa;
        Next(i, u){
            int v = e[i].v; if(v == Fa) continue;
            dep[v] = dep[u] + e[i].w, dfs1(v, u), s[u] += s[v];
            if(s[v] > Max) Max = s[v], son[u] = v;
        }
    }
    void dfs2(int u, int topf){
        top[u] = topf, dfn[u] = ++num, dis[num] = dep[u] - dep[fa[u]]; // dfs 序一定要在 dfs2 记录
        if(son[u]) dfs2(son[u], topf);
        Next(i, u){
            int v = e[i].v; if(v == fa[u] || v == son[u]) continue;
            dfs2(v, v);
        }
    }
    void build(int &p, int l, int r){
        p = ++cnt; if(l == r) return;
        build(ls, l, mid), build(rs, mid + 1, r);
    }
    void update(int &p, int l, int r, int x, int y){
        if(p <= last) t[++cnt] = t[p], p = cnt; // 这样可以节省大量空间,同一颗主席树上不必新开节点
        t[p].sum += dis[y] - dis[x - 1];
        if(l >= x && r <= y){ ++t[p].tag; return;}
        if(y <= mid) update(ls, l, mid, x, y);
        else if(x > mid) update(rs, mid + 1, r, x, y);
        else update(ls, l, mid, x, mid), update(rs, mid + 1, r, mid + 1, y); // 标记永久化必须沿途直接更新答案,因为如果这个地方打上懒标记下次在下面修改时直接继承左右儿子答案是有问题的
    }
    ll query(int p, int l, int r, int x, int y){
        if(!p || l >= x && r <= y) return t[p].sum;
        ll tmp = 1ll * t[p].tag * (dis[y] - dis[x - 1]);
        if(y <= mid) return tmp + query(ls, l, mid, x, y);
        else if(x > mid) return tmp + query(rs, mid + 1, r, x, y);
        else return tmp + query(ls, l, mid, x, mid) + query(rs, mid + 1, r, mid + 1, y);
    }
    void Modify(int x, int w){
        while(top[x] != 1) update(rt[w], 1, n, dfn[top[x]], dfn[x]), x = fa[top[x]];
        update(rt[w], 1, n, 1, dfn[x]);
    }
    ll Ask(int x, int w){
        ll ans = 0;
        while(top[x] != 1) ans += query(rt[w], 1, n, dfn[top[x]], dfn[x]), x = fa[top[x]];
        return ans + query(rt[w], 1, n, 1, dfn[x]);
    }
    int main(){
        n = read(), Q = read(), A = read();
        rep(i, 1, n) a[i].w = read(), a[i].p = i;
        sort(a + 1, a + n + 1);
        rep(i, 1, n - 1) u = read(), v = read(), w = read(), add(u, v, w);
        dfs1(1, 0), dfs2(1, 1);
        rep(i, 1, n) ton[i] = ton[i - 1] + dep[a[i].p];
        rep(i, 1, n) dis[i] += dis[i - 1];
        build(rt[0], 1, n); rep(i, 1, n) rt[i] = rt[i - 1], last = cnt, Modify(a[i].p, i); // 需要在主席树上修改多次时只能这么写
        while(Q--){
            u = read(), x = read(), y = read();
            l = min((x + ans % A) % A, (y + ans % A) % A), r = max((x + ans % A) % A, (y + ans % A) % A);
            x = l, y = r;
            r = upper_bound(a + 1, a + n + 1, (node){y, n}) - a - 1;
            l = lower_bound(a + 1, a + n + 1, (node){x, 0}) - a; // 这里不能先离散化年龄再查
            ans = 1ll * (r - l + 1) * dep[u] + ton[r] - ton[l - 1] - 2ll * (Ask(u, r) - Ask(u, l - 1)); // 不需要直接访问区间信息时直接前缀和更方便
            printf("%lld
    ", ans);
        }
        return 0;
    }
    
    GO!
  • 相关阅读:
    android studio Cannot resolve symbol '@drawable/XXX'等问题解决办法
    android面试
    Android动态增量
    客户端传递Cookie到WebView中
    Android事件传递机制
    Activity状态保存的两种方式
    Activity Window View之间的关系
    Git学习笔记
    Android Log命令常用方法
    ListView 中点击Item中的Button删除当前行
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13500488.html
Copyright © 2011-2022 走看看