BZOJ 3626 LNOI 2014 LCA 树链剖分

75 篇文章 1 订阅
13 篇文章 0 订阅

q个询问统计编号[l,r]内的点与某个点z的LCA的深度和。

任何点与z的LCA都在z到根的路径上,若某个需要统计的点在z的某个祖先的另外的子树中,那么该点对答案的贡献值为该祖先的深度。。

要统计的点在z的子树中:

ans=dep[z]×sz[z]

要统计的点不在z的子树中:
ans=i is the ancestor of zdep[fa[i]]×(sz[fa[i]]sz[i])

求[l,r]可以转化为求[0,r]-[0,l)。
扫描区间右端点,转移:维护新加入的节点的祖先,即祖先的sz全部+1,发现是区间修改,可以考虑树链剖分解决。
那么对于某时候的区间,我们可以快速查询某个点的子树大小,至此我们可以在 O(logn) 的时间内计算一个点的贡献值。重链上的点呢?分析一下性质,发现查询链上的重链上除深度最大的点外,其他的我们需要统计的都必在其轻儿子里。因此我们可以在加点的时候同时维护其到根链上所有相应的点。即不断跳重链。
在查询链上的重链的深度最大的点,有我们询问的节点在其的轻儿子,并不能统计,而其他的更浅的节点,其重儿子在查询链上,那么轻儿子就是其他的子树啦。
因此重链允许区间查询
单次询问复杂度 O(log2n)
总复杂度 O((n+q)log2n)

1A好棒。Rank 16。。。

#include <cstdio>
#include <algorithm>
using namespace std;
const int mod = 201314, N = 50005, M = N * 2;

struct BIT {
    int c[N], n, module;
    void add(int i, int x) {
        for (; i <= n; i += i & -i) {
            c[i] += x;
            if (module) c[i] %= mod;
        }
    }
    void add(int l, int r, int x) {
        add(l, x); add(r + 1, -x);
    }
    int get(int i) {
        int s = 0;
        for (; i; i -= i & -i) {
            s += c[i];
            if (module) s %= mod;
        }
        return s;
    }
    int sum(int i, int j) {
        int ans = get(j) - get(i - 1);
        if (module) ans = (ans % mod + mod) % mod;
        return ans;
    }
} t1, t2;

struct Query {
    int d, p, i, flag;
    friend bool operator< (const Query &a, const Query &b) {
        return a.d < b.d;
    }
} q[N * 2];

int sz[N], h[N], p[M], v[M], ans[N], cnt = 0, id = 0, fa[N], dep[N], son[N], pos[N], top[N];
void add(int a, int b) {
    p[++cnt] = h[a]; v[cnt] = b; h[a] = cnt;
}
void dfs(int x, int f) {
    sz[x] = 1; fa[x] = f; dep[x] = dep[f] + 1; son[x] = 0;
    for (int i = h[x]; i; i = p[i])
        if (v[i] != f) {
            dfs(v[i], x);
            sz[x] += sz[v[i]];
            if (sz[v[i]] > sz[son[x]])
                son[x] = v[i];
        }
}

void dfs2(int x, int t) {
    top[x] = t; pos[x] = ++id;
    if (son[x]) dfs2(son[x], t);
    for (int i = h[x]; i; i = p[i])
        if (v[i] != fa[x] && v[i] != son[x])
            dfs2(v[i], v[i]);
}

void modify(int x) {
    for (; x; x = fa[top[x]]) {
        t1.add(pos[top[x]], pos[x], 1);
        t2.add(pos[x], dep[x]);
    }
}

int query(int x) {
    int ans = 0, last = 0;
    for (; x; x = fa[top[x]]) {
        ans = (ans + dep[x] * (t1.get(pos[x]) - last)) % mod;
        if (x != top[x])
            ans = (ans + t2.sum(pos[top[x]], pos[fa[x]])) % mod;
        last = t1.get(pos[top[x]]);
    }
    return ans;
}

int main() {
    int i, j, n, nq = 0, m, a, b, c;
    scanf("%d%d", &n, &m);
    t2.module = 1;
    t1.n = t2.n = n;
    for (i = 2; i <= n; ++i) {
        scanf("%d", &a);
        add(a + 1, i);
    }
    dfs(1, 0); dfs2(1, 1);
    for (i = 1; i <= m; ++i) {
        scanf("%d%d%d", &a, &b, &c);
        q[++nq] = (Query) { a, c + 1, i, 0 };
        q[++nq] = (Query) { b + 1, c + 1, i, 1 };
    }
    sort(q + 1, q + nq + 1);
    for (i = 1; i <= nq; ++i) {
        if (q[i].d != q[i - 1].d)
            for (j = q[i - 1].d + 1; j <= q[i].d; ++j)
                modify(j);
        j = query(q[i].p);
        if (q[i].flag) ans[q[i].i] += j;
        else ans[q[i].i] -= j;
    }
    for (i = 1; i <= m; ++i)
        printf("%d\n", (ans[i] % mod + mod) % mod);

    return 0;
}

3626: [LNOI2014]LCA

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

Input

第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。

Output

输出q行,每行表示一个询问的答案。每个答案对201314取模输出

Sample Input

5 2
0
0
1
1
1 4 3
1 4 2

Sample Output

8
5

HINT

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值