[GXOI/GZOI2019]旧词 题解

传送门

题意:给一棵树, q q 次询问,每次询问给出 x , y x,y ,求 i = 1 x d e p k ( l c a ( i , y ) ) \sum\limits_{i=1}^x\mathrm{dep}^k(\mathrm{lca}(i,y))
k = 1 k=1 的时候它几乎就是[LNOI2014]LCA。

k = 1 k=1 的做法:

l c a ( i , y ) \mathrm{lca}(i,y) 的深度可以转化为:将 i i 到根的路径上每个点点权都 + 1 +1 ,然后求 y y 到根的路径上的点权和。这个显然可以树剖+线段树解决。所以我们建立可持久化线段树,第 i i 棵线段树表示将 1 1 i i 的每个节点到根的路径上的点权都加了 1 1 的情况,于是可以在线处理询问,只要在第 x x 棵树上求 y y 到根的路径点权和即可。

满分做法:

d e p k ( l c a ( i , y ) ) \mathrm{dep}^k(\mathrm{lca}(i,y)) 可以转化为:对于 i i 到根的路径上的点 u u ,将 u u 的点权增加 d e p k ( u ) ( d e p ( u ) 1 ) k \mathrm{dep}^k(u)-(\mathrm{dep}(u)-1)^k ,这样一来从 i i 到根的路径上的点权总和就增加了
j = 1 d e p ( i ) j k ( j 1 ) k = d e p k ( i ) \sum\limits_{j=1}^{\mathrm{dep}(i)}j^k-(j-1)^k=\mathrm{dep}^k(i)
达到我们的目的了。特别地,可以验证当 k = 1 k=1 j k ( j 1 ) k = 1 j^k-(j-1)^k=1 ,与前面的做法是一致的。所以这里我们的线段树要做的不再是给区间的每个元素加同一个值,而是一段区间中每个元素要加的值固定且不同。实现这个并不困难,预处理出每个位置要加多少以及它的前缀和即可。不明白可以看代码。

以上用可持久化线段树实现的是在线做法,空间略大。本题不强制在线,可以将询问离线后按 x x 排序,只要维护一棵线段树,从小到大依次加入点即可。

#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>

template <typename T> inline void read(T& x) {
    int f = 0, c = getchar(); x = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
    if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
    read(x); read(args...); 
}
template <typename T> void write(T x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }

typedef long long LL;

const LL mod = 998244353;
const int maxn = 5e4 + 207;

inline LL qpow(LL x, LL k) {
    LL s = 1;
    for (; k; x = x * x % mod, k >>= 1)
        if (k & 1) s = s * x % mod;
    return s;
}

int v[maxn], head[maxn], next[maxn], etot;
int dep[maxn], top[maxn], fa[maxn], size[maxn], son[maxn], dfn[maxn], arcdfn[maxn], xys;
LL s[maxn], a[maxn];
int n, q, K;

inline void ae(int x, int y) {
    v[++etot] = y; next[etot] = head[x]; head[x] = etot;
}

void dfs(int x) {
    dep[x] = dep[fa[x]] + 1; size[x] = 1;
    for (int i = head[x]; i; i = next[i]) {
        fa[v[i]] = x;
        dfs(v[i]);
        size[x] += size[v[i]];
        if (size[v[i]] >= size[son[x]]) son[x] = v[i];
    }
}
void dfs(int x, int t) {
    top[x] = t; dfn[x] = ++xys; arcdfn[xys] = x;
    if (son[x]) dfs(son[x], t);
    for (int i = head[x]; i; i = next[i])
        if (v[i] != son[x]) dfs(v[i], v[i]);
}

struct Node {
    int lc, rc;
    LL sum, add;
    Node() : lc(0), rc(0), sum(0), add(0) {}
    
};
Node T[maxn << 7];
int root[maxn], tot;

inline void update(int o) {
    T[o].sum = (T[T[o].lc].sum + T[T[o].rc].sum) % mod;
}
inline void pushdown(int o, int l, int mid, int r) {
    if (T[o].add) {
        LL &d = T[o].add;
        T[++tot] = T[T[o].lc];
        T[o].lc = tot;
        T[++tot] = T[T[o].rc];
        T[o].rc = tot;
        T[T[o].lc].sum = (T[T[o].lc].sum + d * (s[mid] - s[l - 1] + mod) % mod) % mod;
        T[T[o].rc].sum = (T[T[o].rc].sum + d * (s[r] - s[mid] + mod) % mod) % mod;
        T[T[o].lc].add += d;
        T[T[o].rc].add += d;
        d = 0;
    }
}
void modify(int &o, int lb, int rb, int l, int r) {
    if (l > rb || r < lb) return;
    T[++tot] = T[o]; o = tot;
    if (l <= lb && r >= rb) {
        T[o].sum = (T[o].sum + s[rb] - s[lb - 1] + mod) % mod;
        ++T[o].add;
        return;
    }
    int mid = (lb + rb) >> 1;
    pushdown(o, lb, mid, rb);
    modify(T[o].lc, lb, mid, l, r);
    modify(T[o].rc, mid + 1, rb, l, r);
    update(o);
}
LL query(int o, int lb, int rb, int l, int r) {
    if (!o || l > rb || r < lb) return 0;
    if (l <= lb && r >= rb) return T[o].sum;
    int mid = (lb + rb) >> 1;
    pushdown(o, lb, mid, rb);
    return (query(T[o].lc, lb, mid, l, r) + query(T[o].rc, mid + 1, rb, l, r)) % mod;
}

inline void add(int x, int i) {
    for (; x; x = fa[top[x]])
        modify(root[i], 1, n, dfn[top[x]], dfn[x]);
}
inline LL query(int x, int i) {
    LL ans = 0;
    for (; x; x = fa[top[x]])
        ans = (ans + query(root[i], 1, n, dfn[top[x]], dfn[x])) % mod;
    return ans;
}

int main() {
    read(n, q, K);
    for (int i = 2, f; i <= n; ++i)
        read(f), ae(f, i);
    dfs(1); dfs(1, 1);
    for (int i = 1; i <= n; ++i)
        a[i] = (qpow(dep[i], K) - qpow(dep[i] - 1, K) + mod) % mod;
    for (int i = 1; i <= n; ++i)
        s[i] = (s[i - 1] + a[arcdfn[i]]) % mod;
    for (int i = 1; i <= n; ++i) {
        root[i] = root[i - 1];
        add(i, i);
    }
    while (q--) {
        int x, y; read(x, y);
        writeln(query(y, x));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39677783/article/details/89606975