首页 > 其他分享 >【题解】P5305 [GXOI/GZOI2019]旧词

【题解】P5305 [GXOI/GZOI2019]旧词

时间:2023-01-04 21:55:06浏览次数:54  
标签:int 题解 top pos edge dep GZOI2019 res 旧词

题面很清楚,不概括题意了。

思路

树剖。

\(k = 1\) 的情况是 P4211 [LNOI2014]LCA

具体来说,只需要 \(\forall 1 \leq i \leq x\),将 \(1\) 到 \(i\) 的路径上每一个结点权值都加一,然后询问从 \(1\) 到 \(y\) 的路径权值和即可。

具体证明可以画图分讨。

考虑 \(k > 1\) 的情况。

按照 \(k = 1\) 的情况,只需要考虑将 \(dep^k\) 分摊到去往根的路径即可。

根据我不会证,只需要将深度为 \(x\) 的结点增加 \(x^k - (x - 1)^k\)

这里的增量是固定的,所以只需要用线段树维护它的系数即可。

原本的路径加就是路径上的结点系数都加一。

后面按照套路直接按右端点升序排列,然后处理询问即可。

建议酱紫。

代码

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 5e4 + 5;
const int maxe = 5e4 + 5;
const int maxq = 5e4 + 5;
const int mod = 998244353;

struct t_node
{
    int l, r, sum, lazy, val;
} tree[maxn << 2];

struct e_node
{
    int to, nxt;
} edge[maxe];

struct ques
{
    int p, nd, id;

    bool operator < (const ques& rhs) const { return (p < rhs.p); }
} qry[maxq];

int n, q, pw, cnt;
int ans[maxq];
int fa[maxn], son[maxn], top[maxn];
int head[maxn], sz[maxn], pos[maxn], rk[maxn], dep[maxn];

int qpow(int base, int power)
{
    int res = 1;
    while (power)
    {
        if (power & 1) res = 1ll * res * base % mod;
        base = 1ll * base * base % mod;
        power >>= 1;
    }
    return res;
}

void add_edge(int u, int v)
{
    cnt++;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}

void dfs1(int u, int f)
{
    dep[u] = dep[f] + 1;
    sz[u] = 1;
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        dfs1(v, u);
        sz[u] += sz[v];
        if (sz[v] > sz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int t)
{
    top[u] = t;
    pos[u] = ++cnt;
    rk[cnt] = u;
    if (son[u]) dfs2(son[u], t);
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        if ((v != fa[u]) && (v != son[u])) dfs2(v, v);
    }
}

void push_up(int k) { tree[k].sum = (tree[k << 1].sum + tree[k << 1 | 1].sum) % mod; }

void push_down(int k)
{
    if (!tree[k].lazy) return;
    tree[k << 1].sum = (tree[k << 1].sum + 1ll * tree[k].lazy * tree[k << 1].val % mod) % mod;
    tree[k << 1 | 1].sum = (tree[k << 1 | 1].sum + 1ll * tree[k].lazy * tree[k << 1 | 1].val % mod) % mod;
    tree[k << 1].lazy += tree[k].lazy, tree[k << 1 | 1].lazy += tree[k].lazy;
    tree[k].lazy = 0;
}

void build(int k, int l, int r)
{
    tree[k].l = l, tree[k].r = r;
    if (l == r)
    {
        tree[k].val = (qpow(dep[rk[l]], pw) - qpow(dep[rk[l]] - 1, pw) + mod) % mod;
        return;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid);
    build(k << 1 | 1, mid + 1, r);
    tree[k].val = (tree[k << 1].val + tree[k << 1 | 1].val) % mod;
}

void update(int k, int l, int r, int w)
{
    if ((tree[k].l >= l) && (tree[k].r <= r))
    {
        tree[k].sum = (tree[k].sum + 1ll * w * tree[k].val) % mod;
        tree[k].lazy += w;
        return;
    }
    push_down(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if (l <= mid) update(k << 1, l, r, w);
    if (r > mid) update(k << 1 | 1, l, r, w);
    push_up(k);
}

int query(int k, int l, int r)
{
    if ((tree[k].l >= l) && (tree[k].r <= r)) return tree[k].sum;
    push_down(k);
    int mid = (tree[k].l + tree[k].r) >> 1, sum = 0;
    if (l <= mid) sum = (sum + query(k << 1, l, r)) % mod;
    if (r > mid) sum = (sum + query(k << 1 | 1, l, r)) % mod;
    return sum;
}

void modify(int u, int v, int w)
{
    while (top[u] != top[v])
    {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        update(1, pos[top[u]], pos[u], w);
        u = fa[top[u]];
    }
    if (dep[u] > dep[v]) swap(u, v);
    update(1, pos[u], pos[v], w);
}

int queryp(int u, int v)
{
    int res = 0;
    while (top[u] != top[v])
    {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        res = (res + query(1, pos[top[u]], pos[u])) % mod;
        u = fa[top[u]];
    }
    if (dep[u] > dep[v]) swap(u, v);
    res = (res + query(1, pos[u], pos[v])) % mod;
    return res;
}

int main()
{
    scanf("%d%d%d", &n, &q, &pw);
    for (int i = 2; i <= n; i++)
    {
        scanf("%d", &fa[i]);
        add_edge(fa[i], i);
    }
    cnt = 0;
    dfs1(1, 0);
    dfs2(1, 1);
    build(1, 1, n);
    for (int i = 1; i <= q; i++)
    {
        qry[i].id = i;
        scanf("%d%d", &qry[i].p, &qry[i].nd);
    }
    sort(qry + 1, qry + q + 1);
    int cur = 1;
    for (int i = 1; i <= n; i++)
    {
        modify(1, i, 1);
        while ((cur <= q) && (qry[cur].p == i))
        {
            ans[qry[cur].id] = queryp(1, qry[cur].nd);
            cur++;
        }
    }
    for (int i = 1; i <= q; i++) printf("%d\n", (ans[i] % mod + mod) % mod);
    return 0;
}

标签:int,题解,top,pos,edge,dep,GZOI2019,res,旧词
From: https://www.cnblogs.com/lingspace/p/p5305.html

相关文章

  • 题解 校内考20230104 T2 旗木双翼
    题目描述菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。落子......
  • LOJ 数列分块入门 9 题解题报告
    LOJ数列分块入门9题解题报告\(\text{ByDaiRuiChen007}\)I.数列分块入门1题目大意\(\text{Link}\)维护一个长度为\(n\)的序列,支持区间加,单点查值思路分析简......
  • IntelliJ IDEA常见问题解决办法汇总
     mac上idea升级到2020.2.2后,发现versioncontrol中的localchanges不见了!解决办法:View—>ToolWIndows—>Commit【点击下,就会提示要把这个Commit放在IDEA面板那个位置,选择......
  • git 不区分大小写问题解决
    使用vscode   更改vue文件为大驼峰的方式 发现本地代码和提交时的代码名称不同是因为git不区分大小写这时我们需要找到代码存放位置 鼠标右键  GitBashHer......
  • 异地多活回环同步问题解决方案
    1.异地多活:一般为两地三中心或者三地五中心,这样设计是为了在发生单点故障或网络分区时,集群能继续提供服务。两地三中心可以容忍机房级别灾难,三地五中心可以容忍城市级别灾......
  • Codeforces Hello 2023 CF 1779 A~F 题解
    点我看题A.HallofFame先把不用操作就合法的情况判掉。然后发现交换LL,RR,RL都是没用的,只有交换LR是有用的,这样可以照亮相邻的两个位置。所以我们就是要找到一个位置i,......
  • Codeforces Hello 2023 CF 1779 A~F 题解
    点我看题A.HallofFame先把不用操作就合法的情况判掉。然后发现交换LL,RR,RL都是没用的,只有交换LR是有用的,这样可以照亮相邻的两个位置。所以我们就是要找到一个位置i,......
  • 题解 P3526 [POI2011]OKR-Periodicity
    P3526[POI2011]OKR-Periodicitynb题。先说一下这题的入手点。不妨假设一个字符串一定存在一个短周期(约定周期\(p\)满足\(2p\leq|s|\)的为短周期),假设短周期的长度......
  • 寒假第一次洛谷蓝桥个人赛 题解+补题(下)
    D.灵能传输##神仙结论把前缀和应用到极致,6先考虑三个数a,b,c,进行一次变换也就是a+b,-b,c+b可见三个数之和不变,也就是s[3]不变然后之前的s[1]+b,愿称之为s[2]之前的s......
  • P3701 题解
    前言题目传送门!更好的阅读体验?比较简单的最大流基础建图题。以下为了方便,我们把byx称作A,把诗乃酱称作B。思路首先发现,人物的唯一限制就是寿命。所以:从源点向每......