zoukankan      html  css  js  c++  java
  • 投放与花费

    投放与花费

    题目描述

    给你一棵 n 个点的有边权的树,m 次操作,每次有参数 opt, x:

    • opt=1:在 x 点投放一个物品。
    • opt=2:查询点 x 到所有物品的距离和。

    Solution

    对于某个物品的投放点 u ,这次投放会对点 v 产生 (dis_u + dis_v - 2 imes dis_{lca(u,v)}) 的贡献,其中 dis 表示根到该点边权和。

    那么我们每次询问的答案就是 (sum_u dis_u + sum_u dis_v +sum_u dis_{lca(u,v)})

    我们记 sum1 为目前为止投放点的 dis 和,即 (sum1 = sum_u dis_u),sum2 为目前为止的投放次数,即 (sum2 = sum_u1)

    某次查询 v 答案即为 (sum1 +sum2 imes dis_v - 2 imes sum_u dis_{lca(u,v)}),现在问题就是如何快速计算 (sum_u dis_{lca(u,v)})

    其实这个问题单独有个题,可以参考 [LNOI2014]LCA

    做法就是对每个投放点 u,将它到根的路径贡献 +1。查询 v 时查询 v 到根贡献和即为 (dis_{lca(u,v)}),如图:

    a 即为 lca(u,v)。橙色点即为有贡献的点,我们现在对 v 到根求贡献和,就可以求出 (dis_a)

    事情简单了起来,树剖或什么东西维护路径操作,简单查询,答案为

    [ans_v = sum1 + sum2 imes dis_v - 2 imes Ask(v,1) ]

    注意:带权图中线段树要稍微加个权,如下代码中的 t[p].sum。

    (mathbf{Code:})

    #include <bits/stdc++.h>
    #define int long long
    #define swap(x, y)   ((x) ^= (y) ^= (x) ^= (y))
    #define Rep(i, a, b) for (int i = (a), bb = (b); i <= bb; ++i)
    #define S_H(T, i, u) for (int i = T.fl[u], v; v = T.to[i], i; i = T.net[i])
    const int N = 2e5 + 10;
    using namespace std;
    int n, m;
    int a[N], top[N], si[N], d[N], Fa[N], hs[N], pr[N], tr[N], dis[N], vs = 0;
    struct Tree { int to[N << 1], net[N << 1], w[N << 1], fl[N], len;
        inline void inc(int x, int y, int z) { to[++len]=y,net[len]=fl[x],w[len]=z,fl[x]=len; }
    } T;
    struct Sep { int sum, la, val; };
    struct Segmentree { Sep t[N << 2];
        #define ls  (p << 1)
        #define rs  (ls | 1)
        #define mid ((x + y) >> 1)
        #define Ls  ls, x, mid
        #define Rs  rs, mid + 1, y
        inline void Push(int p) { t[p].val = t[ls].val + t[rs].val; }
        inline void Build(int p, int x, int y) { t[p].la = 0;
            if (x == y) return t[p].sum = pr[x], void();
            return Build(Ls), Build(Rs), t[p].sum = t[ls].sum + t[rs].sum, void();
        }
        inline void Update(int p) {
            t[ls].val += t[ls].sum * t[p].la, t[rs].val += t[rs].sum * t[p].la,
            t[ls].la += t[p].la, t[rs].la += t[p].la, t[p].la = 0;
        }
        inline void Modify(int p, int x, int y, int l, int r, int v) {
            if (l > r || x > y || l > y || x > r) return void();
            if (l <= x && y <= r) return t[p].val += t[p].sum * v, t[p].la += v, void();
            Update(p);
            return Modify(Ls, l, r, v), Modify(Rs, l, r, v), Push(p);
        }
        inline int Ask(int p, int x, int y, int l, int r) {
            if (l > r || x > y || x > r || l > y) return 0;
            if (l <= x && y <= r) return t[p].val;
            Update(p);
            return Ask(Ls, l, r) + Ask(Rs, l, r);
        }
    } Se; // definitions
    
    template <class I> inline void read(I &s) {
        char c = getchar(); for (; !isdigit(c); c = getchar());
        for (s = 0; isdigit(c); c = getchar()) s = (s << 3) + (s << 1) + c - 48;
    }
    template <class I> inline void write(I x) {
        (x < 0 ? x = ~x + 1, putchar('-') : 0), (x > 9 ? write(x / 10) : void());
        return putchar(x % 10 + 48), void();
    } // fast io
    
    void Dfs(int u, int fa) { si[u] = 1, d[u] = d[fa] + 1, Fa[u] = fa;
        S_H(T, i, u) { if (v == fa) continue;
            dis[v] = dis[u] + T.w[i], a[v] = T.w[i], Dfs(v, u), si[u] += si[v], (si[hs[u]] < si[v] ? hs[u] = v : 0);
        }
    }
    void Dfs_chain(int u, int k) {
        top[tr[u] = ++vs, u] = k, pr[vs] = a[u]; if (!hs[u]) return void(); Dfs_chain(hs[u], k);
        S_H(T, i, u) { if (v == Fa[u] || v == hs[u]) continue; Dfs_chain(v, v); }
    }
    inline int Lca(int x, int y) {
        while (top[x] ^ top[y]) (d[top[x]] < d[top[y]] ? swap(x, y) : 0), x = Fa[top[x]]; return d[x] < d[y] ? x : y;
    }
    inline void Modify(int x) { while (x) Se.Modify(1, 1, n, tr[top[x]], tr[x], 1), x = Fa[top[x]]; }
    inline int Ask(int x) { int sum = 0; while (x) sum += Se.Ask(1, 1, n, tr[top[x]], tr[x]), x = Fa[top[x]]; return sum; }
    // Tree chain subdivision
    
    signed main(void) {
        freopen("express.in", "r", stdin), freopen("express.out", "w", stdout);
        read(n), read(m);
        Rep(i, 1, n - 1) { int x, y, z; read(x), read(y), read(z), T.inc(x, y, z), T.inc(y, x, z); }
        Dfs(1, 0), Dfs_chain(1, 1), Se.Build(1, 1, n);
        int tmp1 = 0, tmp2 = 0;
        while (m --) {
            int opt, x; read(opt), read(x);
            if (opt == 1) Modify(x), tmp1 += dis[x], ++tmp2;
            else write(tmp1 + tmp2 * dis[x] - Ask(x) * 2), putchar(10);
        }
        return 0;
    } // main
    
    
  • 相关阅读:
    Java中的IO基本用法
    Java中的字符串
    centos
    Linux 常用命令
    rand随机
    auto.js 学习
    毕业了!!!
    论文的查找
    kill的使用
    安装交叉编译工具arm-linux-gcc-4.3.2 并且修改环境变量
  • 原文地址:https://www.cnblogs.com/yywxdgy/p/13776672.html
Copyright © 2011-2022 走看看