zoukankan      html  css  js  c++  java
  • LOJ #6073. 「2017 山东一轮集训 Day5」距离

    #6073. 「2017 山东一轮集训 Day5」距离

    链接

    分析:

      询问$sumlimits_{i in path(u,v)} dis(p_i, k)$,将前面的路径转化一下,分别求$path(u,root), path(v,root), path(lca, root), path(lca, fa[root])$的答案。

      于是现在问题转化为求$sumlimits_{i in path(x,root)} dis(p_i, k)$,我们可以对每个$dis(p_i, k)$分成三部分,$dis(p_i, root) + dis(k,root) -2 imes dis(lca(p_i, k), root)$。

      第一部分可以dfs一个前缀和维护,第二部分是$dep[x] imes dis(k, root)$,那么现在主要的就是求第三部分。

      求一个点与其他所有点的lca的深度,可以看这道题LNOI2014 LCA,而这里不是深度了,是到根的距离,同样为每个点增加一个点权,为这个点上面那条边的边权,同时用树剖线段树维护每个点出现的次数,于是可以用相同的方法做了。

      对每个每个点,都建一棵线段树空间太大,于是可以和可持久化线段树的做法一样,每次只加入修改的线段。

       代码实现上有许多技巧,详见代码。

    代码:

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cctype>
    #include<set>
    #include<queue>
    #include<vector>
    #include<map>
    #include<bitset>
    #define fore(i, u, v) for (int i = head[u], v = e[i].to; i; i = e[i].nxt, v = e[i].to)
    using namespace std;
    typedef long long LL;
    
    inline int read() {
        int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
        for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
    }
    
    const int N = 200005;
    struct Edge { int to, nxt, w; } e[N << 1];
    int head[N], siz[N], son[N], dep[N], bel[N], xl[N], pos[N], fa[N], val[N], ls[N * 100], rs[N * 100], p[N], Root[N];
    int En, TreeIndex, Index, n, NowId;
    LL tsum[N << 2], sum[N * 100], add[N * 100], dis[N], dissum[N];
    
    inline void add_edge(int u,int v,int w) {
        ++En; e[En] = (Edge){v, head[u], w}; head[u] = En;
        ++En; e[En] = (Edge){u, head[v], w}; head[v] = En;
    }
    void dfs1(int u) {
        dep[u] = dep[fa[u]] +1; siz[u] = 1;
        fore(i, u, v) 
            if (v != fa[u]) {
                fa[v] = u, val[v] = e[i].w, dis[v] = dis[u] + e[i].w, dfs1(v);
                siz[u] += siz[v]; son[u] = siz[v] > siz[son[u]] ? v : son[u];
            }
    }
    void dfs2(int u,int top) {
        bel[u] = top, pos[u] = ++Index; xl[Index] = u;
        if (!son[u]) return ;
        dfs2(son[u], top);
        fore(i, u, v) if (v != fa[u] && v != son[u]) dfs2(v, v);
    }
    int LCA(int u,int v) {
        while (bel[u] != bel[v]) 
            dep[bel[u]] > dep[bel[v]] ? u = fa[bel[u]] : v = fa[bel[v]];
        return dep[u] < dep[v] ? u : v;
    }
    void build(int l,int r,int rt) {
        if (l == r) { tsum[rt] = val[xl[l]]; return ; }
        int mid = (l + r) >> 1;
        build(l, mid, rt << 1); build(mid + 1, r, rt << 1 | 1);
        tsum[rt] = tsum[rt << 1] + tsum[rt << 1 | 1];
    }
    void Insert(int l,int r,int rt,int &now,int pre,int L,int R) {
        if (now <= NowId) now = ++TreeIndex, sum[now] = sum[pre], add[now] = add[pre], ls[now] = ls[pre], rs[now] = rs[pre];
        if (L <= l && r <= R) {
            add[now] ++; 
            sum[now] = tsum[rt] * add[now]; 
            if (ls[now]) sum[now] += sum[ls[now]];
            if (rs[now]) sum[now] += sum[rs[now]];
            return ; 
        }
        int mid = (l + r) >> 1;
        if (L <= mid) Insert(l, mid, rt << 1, ls[now], ls[pre], L, R);
        if (R > mid) Insert(mid + 1, r, rt << 1 | 1, rs[now], rs[pre], L, R);
        sum[now] = tsum[rt] * add[now];
        if (ls[now]) sum[now] += sum[ls[now]];
        if (rs[now]) sum[now] += sum[rs[now]];
    }
    LL query(int l,int r,int rt,int now,int L,int R,LL v) {
        if (L <= l && r <= R) return sum[now] + tsum[rt] * v;
        int mid = (l + r) >> 1;
        LL res = 0;
        if (L <= mid) res = query(l, mid, rt << 1, ls[now], L, R, v + add[now]);
        if (R > mid) res += query(mid + 1, r, rt << 1 | 1, rs[now], L, R, v + add[now]);
        return res;
    }
    void update(int x,int y) {
        NowId = TreeIndex;
        int t = x; x = p[x];
        while (x) {
            Insert(1, n, 1, Root[t], Root[y], pos[bel[x]], pos[x]);
            x = fa[bel[x]];
        }
    }
    void solve(int u) {
        dissum[u] = dissum[fa[u]] + dis[p[u]];
        update(u, fa[u]);
        fore (i, u, v) if (v != fa[u]) solve(v);
    }
    LL Calc(int x,int k) {
        LL ans = 0;
        while (k) {
            ans += query(1, n, 1, Root[x], pos[bel[k]], pos[k], 0);
            k = fa[bel[k]];
        }
        return ans;
    }
    LL getans(int x,int k) { 
    return dissum[x] + dis[k] * dep[x] - 2 * Calc(x, k); }
    
    int main() {
        int ty = read();n = read();int m = read();
        for (int u, v, w, i = 1; i < n; ++i) 
            u = read(), v = read(), w = read(), add_edge(u, v, w);
        for (int i = 1; i <= n; ++i) p[i] = read();
        dfs1(1);
        dfs2(1, 1); 
        build(1, n, 1); 
        solve(1);
        LL x, y, k, lastans = 0 ;
        while (m --) {
            x = read(), y = read(), k = read();
            x ^= (ty * lastans), y ^= (ty * lastans), k ^= (ty * lastans);
            int z = LCA(x, y);
            lastans = getans(x, k) + getans(y, k) - getans(z, k) - getans(fa[z], k);
            cout << lastans << "
    ";
        }
        return 0;
    }
     
  • 相关阅读:
    结队完成-连续最大子数组和
    一、数据库的基础简介
    十六、Shell之expect自动化交互程
    十五、Shell之数组
    十四、循环控制
    十三、Shell之select语句
    Linux系统产生随机数的6种方法
    十二、Shell之for循环
    十一、Shell之while&&until循环
    Shell补充之后台执行脚本程序
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10618317.html
Copyright © 2011-2022 走看看