ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

GMOJ 6870. 【2020.11.17提高组模拟】ckw的树 (tree)

2020-11-21 15:31:31  阅读:247  来源: 互联网

标签:17 int mo tree son fa ckw sc sum


ckw的树 (tree)

一类自己很不会做的题目,还是好好写一写 (所以之前TJ都放在总结里是为了水吗)

\(Description\)

给定一棵包含 \(n\) 个节点的树,其中 \(m\) 个点被标记,每单位时间可以从一个点走到距离不超过 \(2\) 的点中(一条边长度为 \(1\)),可以走到自己,求从任意点开始走到标记点的期望时间对 \(998244353\) 取模。


\(\text{Data Constraint}\)

\(2 \leq n \leq 10^5\),\(1 \leq m \leq n\)


\(Solution\)

设从点 \(u\) 出发走到标记点的期望时间为 \(E(u)\)

由于一次转移走到点的距离不能超过 \(2\),所以 \(E(u)\) 可以表示成与 \(u\) 的爷爷、父亲、兄弟、儿子、孙子有关,形式化表达就是:

\[E(u) = \frac{aE(gfa_u) + bE(fa_u) + c \sum{E(bro(u))} + d \sum{E(son(u))} + e \sum E(gson(u))}{d_u} + f \]

其中 \(\text{a, b, c, d, e}\) 分别为我们目前并不确定的系数;\(d_u\) 表示与 \(u\) 距离不大于 \(2\) 的节点数;\(f\) 为同样不确定常数项。

注意,初始时,\(a = b = c = d = e = \frac{1}{d_u}\),以及 \(f = 1\)( \(1\) 表示 \(u\) 需要一单位时间转移,形象地说就是走到对应节点需花费 \(1\) 的时间)

可以暴力地 \(O(n^3)\) 高斯消元,分数 \(20 \sim 40ptes\)。

发现式子中 \(\sum{E(bro(u))}\),\(\sum{E(son(u))}\),\(\sum{E(gson(u))}\) 很难处理,考虑能不能消掉它们。

首先可以观察到叶子节点是没有 后两项 的,那么我们现在考虑对于节点 \(u\),其儿子 \(v_1, \ v_2,\ v_3...\) 只有 \(a, \ b, \ c\) 项(为了方便用 \(a, \ b, \ c\) 表示对应的项),该如何消去其 \(c\) 项。

同样地可以高斯消元暴力消去,但是时间复杂度上不允许,于是让我们细致地观察式子 (雾),可以得到:

\[\sum{E(v_i)} = \sum{a_{v_i}E(gfa_{v_i})} + \sum{b_{v_i}E(fa_{v_i})} + \sum{c_{v_i}\sum{E(bro(v_i))}} + \sum{f_v} \]

其中 \(\sum{E(v_i)} = \sum{E(bro(v_i))}\)(注意 \(v_i \in bro(v_i)\)),\(gfa_{v_i} = fa_u\),\(fa_{v_i} = u\),(也就是儿子的 \(E(gfa_v)\) 相同,\(E(fa_v)\) 也相同)

则我们令 \(\sum{E(bro(v_i))} = sum(u)\),\(\sum{a_{v_i}} = A\),\(\sum{b_{v_i}} = B\),那么 \((2)\) 式可化为:

\[sum(u) = A · E(gfa_v) + B · E(fa_v) + C · sum(u) + \sum{f_v} \]

进一步移项可得:

\[\sum{E(bro_v)} = sum(u) = \frac{A · E(gfa_v) + B · E(fa_v) + \sum{f_v}}{1 - C} \]

将其代入 \(E(v)\) 中即可消去 \(c\) 项。

那么 \(E(son(u)), \ E(gson(u))\) 中就只包含 \(a, \ b\) 项了,我们接着思考如何消去 \(E(u)\) 的 \(d, \ e\) 项 (\(c\) 项会在 \(fa_u\) 处消掉 ),为了方便处理,我们使 \((1)\) 式中 \(E(u)\) 的系数为 \(k\),初始 \(k = 1\),然后分类讨论:

  • 对于 \(son(u)\),其 \(gfa = fa_u, \ fa = u\)。同样设 \(A = \sum{a_v}, \ B = \sum{b_v}, \ F = \sum{f_v}, \ v \in sum(u)\),那么若将 \(E(son(u))\) 代入 \(E(u)\) 中,\(A\) 会贡献到 \(b_u\) 中,\(B\) 会贡献到 \(k\) 中
  • 对于 \(gson(u)\),其 \(gfa = u, \ fa = son(u)\)。同上,设 \(A = \sum{a_{gson(u)}}\),若将 \(E(gson(u))\) 代入 \(E(u)\) 中,\(A\) 会贡献到 \(k\) 中;但与上有区别,对于每个 \(v \in son(u)\),设 \(B'_v = \sum{b_{v'}}, \ v' \in son(v)\),那么 \(B'_{v}\) 会带来一个 \(B'_v · E(v)\) 的项,将 \(E(v)\) 代入再计算一遍即可。

为了方便处理,每次处理 \(E(u)\) 时可以同时计算出 \(B'_u = \sum{b_v}, \ v \in son(u)\);以及为了方便以后的处理,最后要将 \(k\) 变为 \(1\)。

从下往上处理一遍便得到了所有只包含 \(a, \ b, \ f\) 项的 \(E(u)\),若以 \(1\) 为根,那么 \(E(1)\) 只有 \(f\) 项,也就是 \(E(1)\) 已经确定,所以我们只需要从上往下再做一遍,即可求出所有的 \(E(u)\)。

注意标记点的 \(E\) 为 \(0\)


\(Code\)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define N 100000
#define mo 998244353

#define ll long long

#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define Fo(i, u) for(int i = head[u]; i; i = edge[i].next)

void read(int &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
}

struct EDGE { int next, to; } edge[N << 1];

int head[N + 1], son[N + 1], d[N + 1], bz[N + 1];

int n, m;

struct Arr { ll a, b, c, d; } f[N + 1], g[N + 1];

int cnt_edge = 1;
void Add(int u, int v) { edge[ ++ cnt_edge ] = (EDGE) { head[u], v }, head[u] = cnt_edge; }
void Link(int u, int v) { Add(u, v), Add(v, u); }

ll Fast(ll x, int p = mo - 2) {
    ll res = 1;
    while (p) {
        if (p & 1) (res *= x) %= mo;
        (x *= x) %= mo;
        p >>= 1;
    }
    return res;
}

void Dfs1(int u, int fa, int ff, int la) {
    d[u] = son[fa] + (fa > 0) + (ff > 0);
    son[u] = 0;
    Fo(i, u) if (i != la) ++ son[u];
    ll A = 0, B = 0, C = 1, D = 0;
    int v = 0;
    Fo(i, u) if (i != la) {
        v = edge[i].to;
        Dfs1(v, u, fa, i ^ 1);
        d[u] += son[v] + 1;
        (A += f[v].a) %= mo, (B += f[v].b) %= mo, (C += mo - f[v].c) %= mo, (D += f[v].d) %= mo;
    }

    int sd = Fast(d[u]);
    f[u].a = f[u].b = f[u].c = sd, f[u].d = 1;
    ll K = 1;

    ll sc = Fast(C);
    if (! C) sc = 0;
    A = A * sc % mo, B = B * sc % mo, D = D * sc % mo;
    Fo(i, u) if (i != la) {
        v = edge[i].to;
        (f[v].a += f[v].c * A) %= mo;
        (f[v].b += f[v].c * B) %= mo;
        (f[v].d += f[v].c * D) %= mo;
        f[v].c = 0;

        (f[u].b += f[v].a * (g[v].b + 1) % mo * sd % mo) %= mo;
        (f[u].d += f[v].d * (g[v].b + 1) % mo * sd % mo) %= mo;
        (K += mo - f[v].b * (g[v].b + 1) % mo * sd % mo) %= mo;

        (f[u].d += g[v].d * sd % mo) %= mo;
        (K += mo - g[v].a * sd % mo) %= mo;

        (g[u].a += f[v].a) %= mo, (g[u].b += f[v].b) %= mo, (g[u].d += f[v].d) %= mo;
    }
    if (! bz[u]) {
        ll sk = Fast(K);
        (f[u].a *= sk) %= mo, (f[u].b *= sk) %= mo, (f[u].c *= sk) %= mo, (f[u].d *= sk) %= mo;
    } else
        f[u].a = f[u].b = f[u].c = f[u].d = 0;
}

ll ans[N + 1];

void Dfs2(int u, int fa, int ff, int la) {
    ans[u] = ((f[u].a * ans[ff] % mo + f[u].b * ans[fa] % mo) % mo + f[u].d) % mo;
    Fo(i, u) if (i != la)
        Dfs2(edge[i].to, u, fa, i ^ 1);
}

int main() {
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);

    read(n), read(m);
    int x, y;
    fo(i, 2, n) read(x), read(y), Link(x, y);
    fo(i, 1, m) read(x), bz[x] = 1;

    son[0] = 1;
    Dfs1(1, 0, 0, 0);

    ll sc = Fast((1 - f[1].c + mo) % mo);
    (f[1].a *= sc) %= mo, (f[1].b *= sc) %= mo, f[1].c = 0, (f[1].d *= sc) %= mo;
    Dfs2(1, 0, 0, 0);

    fo(i, 1, n) printf("%d\n", ans[i]);

    return 0;
}

标签:17,int,mo,tree,son,fa,ckw,sc,sum
来源: https://www.cnblogs.com/zhouzj2004/p/14015812.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有