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

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

链接

分析:

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

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

  第一部分可以dfs一个前缀和维护,第二部分是$dep[x] \times 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 << "\n";
    }
    return 0;
}

 

 

 

转载于:https://www.cnblogs.com/mjtcn/p/10618317.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值