树剖top数组

好像很少有关于top数组性质的博客。

for(int i=top[x];fa[i];i=top[fa[i]])

可以遍历所有子树中含有x节点的重链链头节点(或叶子节点),由于重链不超过log(n)个,所以这个循环复杂度是O(logN)的。
设i是重链链头节点,f是i的父节点,则i是f的轻儿子。
所以,如果有操作:对于x节点,他的所有儿子均加上不同的值,且这个值只与儿子节点自身和提供的值有关,那么,可以只更新x节点的重儿子,在x节点加上标记,查询的时候加上答案即可。

D. Tree Queries

Hanh is a famous biologist. He loves growing trees and doing experiments on his own garden.

One day, he got a tree consisting of n vertices. Vertices are numbered from 1 to n. A tree with n vertices is an undirected connected graph with n−1 edges. Initially, Hanh sets the value of every vertex to 0.

Now, Hanh performs q operations, each is either of the following types:

Type 1. Hanh selects a vertex v and an integer d. Then he chooses some vertex r uniformly at random, lists all vertices u such that the path from r to u passes through v. Hanh then increases the value of all such vertices u by d.
Type 2. Hanh selects a vertex v and calculates the expected value of v.
Since Hanh is good at biology but not math, he needs your help on these operations.

Input
The first line contains two integers n and q (1≤n,q≤150000) — the number of vertices on Hanh’s tree and the number of operations he performs.

Each of the next n−1 lines contains two integers u and v (1≤u,v≤n), denoting that there is an edge connecting two vertices u and v. It is guaranteed that these n−1 edges form a tree.

Each of the last q lines describes an operation in either formats:

1 v d ( 1 ≤ v ≤ n , 0 ≤ d ≤ 1 0 7 ) 1 v d (1≤v≤n,0≤d≤10^7) 1vd(1vn,0d107), representing a first-type operation.
2 v ( 1 ≤ v ≤ n ) 2 v (1≤v≤n) 2v(1vn), representing a second-type operation.
It is guaranteed that there is at least one query of the second type.

Output
For each operation of the second type, write the expected value on a single line.

Let M=998244353, it can be shown that the expected value can be expressed as an irreducible fraction pq, where p and q are integers and q≢0(modM). Output the integer equal to p⋅q−1modM. In other words, output such an integer x that 0≤x<M and x⋅q≡p(modM).

Example
input

5 12
1 2
1 3
2 4
2 5
1 1 1
2 1
2 2
2 3
2 4
2 5
1 2 2
2 1
2 2
2 3
2 4
2 5

output

1
199648871
399297742
199648871
199648871
598946614
199648873
2
2
2

分析r相对于v的位置容易得如何更新答案,对给定节点v,需要对整棵树操作一次和子树v操作一次以及对v的每个子节点u,减去d*siz[son[u]]*inv,这样做是O(N)的复杂度,只能过11个点,当然,也可以按子树大小分块,复杂度降到了 O ( N ) O(\sqrt N) O(N ),但是好像码量有些大。
所以,可以只更新v节点的重儿子,轻儿子的更新标记放置在v节点上,因为轻儿子一定是重链头结点或叶子节点,所以查询的时候减去多余的答案即可。

#include<bits/stdc++.h>
using namespace std;
char buf[1<<20],*_=buf,*__=buf;
#define gc() (_==__&&(__=(_=buf)+fread(buf,1,1<<20,stdin),_==__)?EOF:*_++)
#define TT template<class T>inline
TT bool read(T &x){
    x=0;char c=gc();bool f=0;
    while(c<48||c>57){if(c==EOF)return 0;f^=(c=='-'),c=gc();}
    while(47<c&&c<58)x=(x<<3)+(x<<1)+(c^48),c=gc();
    if(f)x=-x;return 1;
}
TT bool read(T&a,T&b){return read(a)&&read(b);}
TT bool read(T&a,T&b,T&c){return read(a)&&read(b)&&read(c);}
typedef long long ll;
const ll MAXN=1e5+8,mod=1e9+8,inf=0x3f3f3f3f;
#define lowbit(x) (x&(-x))
#define Max(a,b) if(b>a)a=b
#define Min(a,b) if(b<a)a=b
int n;
struct E{int y,nt;}e[MAXN<<1];
int head[MAXN],cnt;
inline void add(int x,int y){
    e[++cnt].y=y;
    e[cnt].nt=head[x];
    head[x]=cnt;
}
int dep[MAXN],siz[MAXN],fa[MAXN],son[MAXN];
void dfs1(int x){
    siz[x]=1,dep[x]=dep[fa[x]]+1;
    for(int i=head[x];i;i=e[i].nt){
        int y=e[i].y;
        if(siz[y])continue;
        fa[y]=x;
        dfs1(y);
        siz[x]+=siz[y];
        if(!son[x]||siz[son[x]]<siz[y])son[x]=y;
    }
}
int id[MAXN],top[MAXN],idcnt;
ll d[MAXN];
void dfs2(int x,int tp){
    id[x]=++idcnt;
    top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i;i=e[i].nt){
        int y=e[i].y;
        if(id[y])continue;
        dfs2(y,y);
    }
}
inline void presum_add(int p,ll v){
    while(p<=n){
        (d[p]+=v)%=mod;
        p+=lowbit(p);
    }
}
inline ll ask(int p){
    ll res=0;
    while(p){
        (res+=d[p])%=mod;
        p-=lowbit(p);
    }return res;
}
inline void subtree_add(const int x,const ll v){
    presum_add(id[x],v);
    presum_add(id[x]+siz[x],mod-v);
}
ll qpow(ll x,ll k){
    ll res=1;
    while(k){
        if(k&1)res=res*x%mod;
        x=x*x%mod;k>>=1;
    }return res;
}
ll lazy[MAXN];
int main() {
    int q;
    read(n,q);
    for(int i=1,x,y;i<n;++i){
        read(x,y);
        add(x,y);
        add(y,x);
    }
    dfs1(1);
    dfs2(1,1);
    int op,x;
    ll inv=qpow(n,mod-2),val;
    while(q--){
        read(op,x);
        if(op==1){
            read(val);
            (lazy[x]+=val)%=mod;
            subtree_add(x,(n-siz[x])*val%mod);
            subtree_add(1,siz[x]*val%mod);
            if(!son[x])continue;
            subtree_add(son[x],mod-siz[son[x]]*val%mod);
        }
        else{
            ll res=ask(id[x]);
            for(int i=top[x];fa[i];i=top[fa[i]]){
                (res+=mod-siz[i]*lazy[fa[i]]%mod)%=mod;
            }
            printf("%I64d\n",res*inv%mod);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值