首页 > 其他分享 >CF1254D Tree Queries

CF1254D Tree Queries

时间:2023-12-30 21:45:21浏览次数:29  
标签:CF1254D val int siz inv Tree dfn Queries mint

Tree Queries

Luogu CF1254D

题面翻译

给定一棵 \(N\) 个节点的树,有 \(Q\) 次操作。

  • \(1\ v\ d\) 给定一个点 \(v\) 和一个权值 \(d\),等概率地选择一个点 \(r\),对每一个点 \(u\),若 \(v\) 在 \(u\) 到 \(r\) 的路径上,则 \(u\) 的权值加上 \(d\)(权值一开始为 \(0\))。
  • \(2\ v\) 查询 \(v\) 的权值期望,对 \(998244353\) 取模。

\(1\leq N,Q \leq 150000\)

Solution

操作很怪异,所以考虑一下每次操作会对所有点产生什么影响。先选中一个点 \(v\) 作为操作点,然后枚举点 \(r\)。容易发现最后产生的影响只与 \(v\) 作为树根时 \(r\) 在 \(v\) 的哪一个子树有关,那么这一次操作如果选择 \(r\) 将会对 \(v\) 除 \(r\) 所在的子树外的所有子树产生 \(\dfrac d n\) 的贡献,也就是说这一颗子树内所有可能的 \(r\) 点将会对其余所有的子树产生 \(\dfrac {ds}{n}\) 的贡献(其中 \(s\) 为当前子树的大小)。

那么可以有两种不同思路的暴力,一种修改慢查询快,一种修改快查询慢:

  • 考虑对每一个节点 \(x\) 维护一个值 \(val_x\) 表示当前节点的答案。那么每次修改就是暴力枚举修改节点的每一个子树,然后暴力的加到所有节点的 \(v_x\) 上。
  • 每次修改的时候暴力枚举所有修改过的点,然后根据修改的这个点在询问点的哪一个子树来计算答案。

注意到我们每一次的修改都与当前节点有的子树个数相关,因此对度数根号分治,设一个阈值 \(S\),所有度数大于 \(S\) 的节点称为关键点。显然关键点的数量是 \(\mathcal O(\dfrac n S)\) 的。

对于修改操作,如果当前点是关键点,那么就只把当前点标记上;否则直接暴力枚举所有子树然后处理贡献。注意到处理贡献的时候可以看作是子树加,所以使用树状数组来维护。复杂度是 \(\mathcal O(S\log n)\)。

对于询问操作,先在树状数组中取出非关键点的贡献,然后再暴力枚举所有关键点并计算其贡献。时间复杂度不精细实现是 \(\mathcal O(\dfrac n S\log n)\)。

\(S\) 取 \(\sqrt n\) 时最优,总时间复杂度 \(\mathcal O(q\sqrt n\log n)\),跑得挺慢,不过能过。

Code
int N, Q, S;
vector<int> e[_N];
bool crit[_N];
int deg[_N];
vector<int> crl;
int fa[20][_N], dep[_N], siz[_N], dfn[_N], tot;
void dfs(int x, int F) {
    fa[0][x] = F, siz[x] = 1, dfn[x] = ++tot, dep[x] = dep[F] + 1;
    For(i, 1, 18) fa[i][x] = fa[i-1][fa[i-1][x]];
    for (int v: e[x]) if (v ^ F) dfs(v, x), siz[x] += siz[v];
}
int jump(int x, int y) {
    for (int tmp = dep[x] - dep[y] - 1, i = 0; tmp; tmp >>= 1, ++i)
        if (tmp & 1) x = fa[i][x];
    return x;
}
struct Bit {
    mint val[_N];
    inline int lowbit(int x) {return x & -x;}
    void update(int x, mint v) {for (; x <= N; x += lowbit(x)) val[x] += v;}
    inline void update(int l, int r, mint v) {update(l, v), update(r + 1, -v);}
    mint ask(int x) {mint res = 0; for (; x; x -= lowbit(x)) res += val[x]; return res;}
} bit;
inline bool chk(int x, int y) {return dfn[x] >= dfn[y] && dfn[x] < dfn[y] + siz[y];}
mint inv, val[_N];
void _() {
    cin >> N >> Q;
    inv = mint(1) / N;
    For(i, 2, N) {
        int x, y; cin >> x >> y;
        e[x].epb(y), e[y].epb(x);
        ++deg[x], ++deg[y];
    }
    S = sqrt(N);
    For(i, 1, N) if (deg[i] >= S)
        crl.epb(i), crit[i] = 1;
    Debug("crit:", crl);
    dfs(1, 0);
    while (Q--) {
        int opt, x;
        cin >> opt >> x;
        if (opt == 1) {
            mint d; cin >> d;
            if (crit[x]) val[x] += d;
            else {
                for (int v: e[x]) {
                    if (fa[0][x] == v) {
                        mint res = (N - siz[x]) * d * inv;
                        bit.update(dfn[x], dfn[x] + siz[x] - 1, res);
                    } else {
                        mint res = siz[v] * d * inv;
                        bit.update(1, N, res);
                        bit.update(dfn[v], dfn[v] + siz[v] - 1, -res);
                    }
                }
                bit.update(1, N, d * inv);
            }
        } else {
            mint base = bit.ask(dfn[x]);
            for (int c: crl) {
                if (x == c) base += val[c];
                else if (chk(x, c)) {
                    int u = jump(x, c);
                    base += val[c] * (N - siz[u]) * inv;
                } else base += val[c] * siz[c] * inv;
            }
            fmtout("{}\n", base);
        }
    }
}

标签:CF1254D,val,int,siz,inv,Tree,dfn,Queries,mint
From: https://www.cnblogs.com/hanx16msgr/p/17936858

相关文章

  • CF1320E Treeland and Viruses
    TreelandandVirusesLuoguCF1320E题面翻译有一棵有\(n\)个节点的树,\(q\)次询问(询问互相独立),每次给定\(k_i\)个颜色,每个颜色有一个起始点\(v_j\)和移动速度\(s_j\),每一个颜色在每一次操作中会使它周围没有被染色的连通块上与它的距离不超过\(s_j\)的点全部染为这一......
  • B - Christmas Trees
    B-ChristmasTreeshttps://atcoder.jp/contests/abc334/tasks/abc334_b 思路对于起始种树点A在[L,R]区间的位置情况,三种A<LA>RA>=L,A<=R Codehttps://atcoder.jp/contests/abc334/submissions/48822474LLa,m,l,r;intmain(){cin>>a>&g......
  • CF1917F Construct Tree 题解
    Description给你一个数组\(l_1,l_2,\dots.l_n\)和一个数字\(d\)。问你是否能够构造一棵树满足以下条件:这棵树有\(n+1\)个点。第\(i\)条边的长度是\(l_i\)。树的直径是\(d\)。只需要判断是否有解即可。\(2\len\le2000,1\led\le2000,1\lel_i\led\)。Solutio......
  • Maximum And Queries (hard version)
    题目传送门感觉这题比\(\rmF\)难啊,\(\rmF\)就是个板子,但为啥这题是蓝的,\(\rmF\)是紫的。思路首先考虑\(nq\)怎么做。发现很简单,按位贪心就行了。具体地说,从大到小枚举二进制位,判断答案中能否出现这一位,若\(i\)当前这一位没有值,那么必须被补全到这个值,否则无所谓,然......
  • CF1917F Construct Tree 题解
    题目链接:https://codeforces.com/contest/1917/problem/F题意有\(n\)条长度\(l_i\)的边,问它们是否能组成一棵\(n+1\)个节点的树,使得树的直径长度为\(d\)。\(n,d\le2000\)。题解首先当然要存在一个边集\(D\),使得\(\sum\limits_{i\inD}l_i=d\),这可以使用背包......
  • Layui treeTable 使用【数据不显示、子级不显示】
    //返回JSON数据类publicclassLeaveMessageTreeTable{publicLeaveMessageTreeTable(){this.children=newList<LeaveMessageTreeTable>();this.isParent=false;}publicintId{get;set;}publicintUserId{get;s......
  • CF342E Xenia and Tree
    题意给定一棵\(n\)个节点的一棵树,初始时\(1\)号点为红色,其余为蓝色。要求支持以下操作:将一个节点变为红色。询问节点\(u\)到最近红色节点的距离共\(q\)次操作。Sol喵喵题。不难想到点分树做法,不再阐述。考虑简单的操作分块。对于块外,可以考虑每做完一个块就......
  • layui 树组件tree 通过API获取数据
    一、简单vartreedata=[]; tree.render({ elem:'#addLeftType', id:'demoId', data:treedata, showCheckbox:true, oncheck:function(obj){ console.log(obj.data);//得到当前点击的节点数据 console.log(obj.checked);//节点是否被选中 console.l......
  • 【CF1917F】Construct Tree
    题目题目链接:https://codeforces.com/contest/1917/problem/F给出\(n\)条边的边权,询问是否可以构造出一棵树,使得所有边都被用上恰好一次且直径为\(d\)。\(n,d\leq2000\)。思路首先肯定是找出一条长度为\(d\)的链,然后判断可不可以把剩下的所有边都挂在这条链的带权重心......
  • CF396C On Changing Tree 题解
    CF396C考虑将贡献表示出来:\(\forallv\in\text{subtree}_u\),\(v\)会加上\(x-(dep_v-dep_u)k\),然后发现这个东西可以维护整棵子树,即把\(x,dep_u\timesk\)和\(dep_v\timesk\)分开计算,然后\(dep_v\)可以最后再管,就变为子树加,单点和了。用树状数组维护dfs序即可。......