Luogu P4211 [LNOI2014]LCA 题解

Link

Description

给出一个 nn 个节点的有根树(编号为 \(0\)\(n-1\),根节点为 \(0\))。

一个点的深度定义为这个节点到根的距离 \(+1\)

\(dep[i]\) 表示点i的深度,\(LCA(i,j)\) 表示 \(i\)\(j\) 的最近公共祖先。

\(m\) 次询问,每次询问给出 \(l\ r\ z\),求 \(\sum_{i=l}^r dep[LCA(i,z)]\)

\(1\le n,m \le 50000\)

Solution

考虑暴力,枚举 \([l,r]\),计算 \(lca\) 的深度和。

那么怎么优化呢,由于深度就是到根的距离,所以可以把 \([l,r]\) 中每个点到根的链上的权值都 \(+1\),然后答案就是 \(z\) 到根的链的权值和。

因为对于每个点,与 \(z\)\(lca\) 的深度就是 \(lca\) 到根的链上权值都 \(+1\)

链上操作可以用树剖维护,复杂度 \(O(nm\log n)\),依然不行。

考虑怎么将 \(m\) 优化掉。

对于每个询问 \([l,r]\),可以利用差分的思想,答案为 \([1,r] - [1,l-1]\)。将 \(l,r\) 分别存进数组,并标记是左端点还是右端点,然后按 \(1\)\(n\) 的顺序更新链上的权值,然后在询问处记下链上的和,最后相减即可。

Code

#include <bits/stdc++.h>

using namespace std;

template <typename T>
void read(T &x)
{
    x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
    return;
}

template <typename T>
void write(T x)
{
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}

const int N = 5e4 + 5;
const int p = 201314;
int n, m, fa[N];
vector <int> g[N];
struct Query
{
    int pos, z;
    bool flag;
    int id;
    friend bool operator < (Query x, Query y)
    {
        return x.pos < y.pos;
    }
}q[N << 1];
int cnt;
int L[N], R[N];

int siz[N], dep[N], son[N];
void dfs1(int u)
{
    dep[u] = dep[fa[u]] + 1, siz[u] = 1;
    for(int i = 0; i < (int)g[u].size(); i++)
    {
        int v = g[u][i];
        dfs1(v);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
    return;
}

int top[N], id[N], tot;
void dfs2(int u, int tp)
{
    top[u] = tp, id[u] = ++tot;
    if(!son[u]) return;
    dfs2(son[u], tp);
    for(int i = 0; i < (int)g[u].size(); i++)
    {
        int v = g[u][i];
        if(v != son[u]) dfs2(v, v);
    }
    return;
}

#define ls rt << 1
#define rs rt << 1 | 1

int sum[N << 2], tag[N << 2];
void pushup(int rt)
{
    sum[rt] = sum[ls] + sum[rs];
}

void pushdown(int l, int r, int rt)
{
    if(tag[rt])
    {
        int mid = (l + r) >> 1;
        sum[ls] = (sum[ls] + tag[rt] * (mid - l + 1)) % p;
        sum[rs] = (sum[rs] + tag[rt] * (r - mid)) % p;
        tag[ls] = (tag[ls] + tag[rt]) % p;
        tag[rs] = (tag[rs] + tag[rt]) % p;
        tag[rt] = 0;
    }
}

void upd(int L, int R, int v, int l, int r, int rt)
{
    if(l > R || r < L) return;
    if(L <= l && r <= R)
    {
        sum[rt] = (sum[rt] + v * (r - l + 1)) % p;
        tag[rt] = (tag[rt] + v) % p;
        return;
    }
    pushdown(l, r, rt);
    int mid = (l + r) >> 1;
    upd(L, R, v, l, mid, ls);
    upd(L, R, v, mid + 1, r, rs);
    pushup(rt);
}

int qry(int L, int R, int l, int r, int rt)
{
    if(l > R || r < L) return 0;
    if(L <= l && r <= R) return sum[rt];
    pushdown(l, r, rt);
    int mid = (l + r) >> 1;
    return (qry(L, R, l, mid, ls) + qry(L, R, mid + 1, r, rs)) % p;
}

void update(int x, int val)
{
    while(x)
    {
        upd(id[top[x]], id[x], val, 1, n, 1);
        x = fa[top[x]];
    }
    return;
}

int query(int x)
{
    int res = 0;
    while(x)
    {
        res = (res + qry(id[top[x]], id[x], 1, n, 1)) % p;
        x = fa[top[x]];
    }
    return res;
}

int main()
{
    read(n), read(m);
    for(int i = 2; i <= n; i++)
    {
        read(fa[i]), fa[i]++;
        g[fa[i]].push_back(i);
    }
    dfs1(1), dfs2(1, 1);
    for(int i = 1; i <= m; i++)
    {
        int l, r, z;
        read(l), read(r), read(z);
        l++, r++, z++;
        q[++cnt] = (Query){l - 1, z, 0, i};
        q[++cnt] = (Query){r, z, 1, i};
    }
    sort(q + 1, q + 1 + cnt);
    int now = 1;
    for(int i = 1; i <= cnt; i++)
    {
        while(now <= q[i].pos) update(now, 1), now++;
        if(!q[i].flag) L[q[i].id] = query(q[i].z);
        else R[q[i].id] = query(q[i].z);
    }
    for(int i = 1; i <= m; i++)
        write((R[i] - L[i] + p) % p), puts("");
    return 0;
}
posted @ 2021-10-13 21:39  Acestar  阅读(57)  评论(0编辑  收藏  举报