luogu 4211

题意

存在一棵树,每次询问 \(l, r, z\)

\[\sum_{i = l} ^ {r} deep(lca(i, z))\]


1268025-20180914095619029-1607674217.png

考虑 lca 的实质:两点到根的路径的交集中深度最大的点
其中一点到根的路径上经过的存在于另一点到根的路径上的点一定存在于 lca 到根的路径上
这样的话,对 \([l, r]\) 内的每个点,在其到根的路径上的权 + 1;
查询答案时,就是询问 \(z\) 到根节点的价值和

统计答案是有个小技巧
首先将所有的询问的 \(l, r\) 存到数组中,从小到大扫
如果是某个询问的左端点,查询并记录
如果是右端点,查询,减去相应的左端点就是该询问的答案


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>

using namespace std;

const int N = 5e4 + 10;
const int Mod = 201314;

#define LL long long

#define int long long

int topp[N], fa[N], size[N], son[N], deep[N], tree[N];
int Tree;
LL W[N << 2], F[N << 2], S[N << 2];
struct Node {int u, v, nxt;} G[N << 1];
int now, head[N];
int n, q;
struct Node_ {
    int l, r, z, id;
} Ask[N];

struct Node_2 {
    int l, opt, id;
} Ask2[N << 1];

#define gc getchar()

inline int read() {
    int x = 0; char c = gc;
    while(c < '0' || c > '9') c = gc;
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
    return x;
}

void Link(int u, int v) {G[++ now].v = v; G[now].nxt = head[u]; head[u] = now;}

void Dfs_1(int u, int f_, int dep) {
    fa[u] = f_, deep[u] = dep, size[u] = 1;
    for(int i = head[u]; ~ i; i = G[i].nxt) {
        int v = G[i].v;
        if(v == f_) continue;
        Dfs_1(v, u, dep + 1);
        size[u] += size[v];
        if(size[son[u]] < size[v]) son[u] = v;
    }
}

void Dfs_2(int u, int tp) {
    topp[u] = tp, tree[u] = ++ Tree;
    if(!son[u]) return ;
    Dfs_2(son[u], tp);
    for(int i = head[u]; ~ i; i = G[i].nxt) {
        int v = G[i].v;
        if(v != fa[u] && v != son[u]) Dfs_2(v, v);
    }
}

#define lson jd << 1
#define rson jd << 1 | 1

void Build_tree(int l, int r, int jd) {
    S[jd] = r - l + 1;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    Build_tree(l, mid, lson), Build_tree(mid + 1, r, rson);
}

void Push_down(int jd) {
    (F[lson] += F[jd]) %= Mod, (F[rson] += F[jd]) %= Mod;;
    (W[lson] += S[lson] * F[jd]) %= Mod, (W[rson] += S[rson] * F[jd]) %= Mod;
    F[jd] = 0;
}

void Sec_G(int l, int r, int jd, int x, int y, int opt) {
    if(x <= l && r <= y) {
        (F[jd] += opt) %= Mod;
        (W[jd] += S[jd] * opt) %= Mod;
        return ;
    }
    if(F[jd]) Push_down(jd);
    int mid = (l + r) >> 1;
    if(x <= mid) Sec_G(l, mid, lson, x, y, opt);
    if(y > mid)  Sec_G(mid + 1, r, rson, x, y, opt);
    W[jd] = W[lson] + W[rson];
}

void Sec_G_imp(int x, int opt) {
    int tpx = topp[x];
    while(tpx != 1) {
        Sec_G(1, n, 1, tree[tpx], tree[x], opt);
        x = fa[tpx], tpx = topp[x];
    }
    Sec_G(1, n, 1, tree[1], tree[x], opt);
}

int ret;

void Sec_A(int l, int r, int jd, int x, int y) {
    if(x <= l && r <= y) {
        (ret += W[jd]) %= Mod;
        return ;
    }
    if(F[jd]) Push_down(jd);
    int mid = (l + r) >> 1;
    if(x <= mid) Sec_A(l, mid, lson, x, y);
    if(y > mid)  Sec_A(mid + 1, r, rson, x, y);
}

inline int Sec_A_imp(int x) {
    int tpx = topp[x];
    ret = 0;
    while(tpx != 1) {
        Sec_A(1, n, 1, tree[tpx], tree[x]);
        x = fa[tpx], tpx = topp[x];
    }
    Sec_A(1, n, 1, tree[1], tree[x]);
    return ret % Mod;
}

int Answer[N];
int PreSum[N];

inline bool Cmp(Node_2 a, Node_2 b) {
    return a.l < b.l;
}

main() {
    n = read(), q = read();
    for(int i = 1; i <= n; i ++) head[i] = -1;
    for(int i = 1; i < n; i ++) {
        int u = read() + 1;
        Link(u, i + 1), Link(i + 1, u);
    }
    Dfs_1(1, 0, 1);
    Dfs_2(1, 1);
    Build_tree(1, n, 1);
    for(int i = 1; i <= q; i ++)
        Ask[i].l = read() + 1, Ask[i].r = read() + 1, Ask[i].z = read() + 1, Ask[i].id = i;
    int js = 0;
    for(int i = 1; i <= q; i ++) {
        Ask2[++ js].l = Ask[i].l - 1; Ask2[js].id = i; Ask2[js].opt = 1;
        Ask2[++ js].l = Ask[i].r; Ask2[js].id = i; Ask2[js].opt = 0;
    }
    sort(Ask2 + 1, Ask2 + js + 1, Cmp);
    int L = 0;
    for(int i = 1; i <= js; i ++) {
        while(L < Ask2[i].l) {
            L ++;
            Sec_G_imp(L, 1);
        }
        if(Ask2[i].opt == 1) {
            PreSum[Ask2[i].id] = Sec_A_imp(Ask[Ask2[i].id].z);  
        } else {
            Answer[Ask2[i].id] = Sec_A_imp(Ask[Ask2[i].id].z);
            Answer[Ask2[i].id] -= PreSum[Ask2[i].id];
            (Answer[Ask2[i].id] += Mod) %= Mod;
        }
    }
    for(int i = 1; i <= q; i ++) cout << Answer[i] << "\n";
    return 0;
}

转载于:https://www.cnblogs.com/shandongs1/p/9644876.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值