出题人的做法是 \(O(n\sqrt{n\log n})\),结果这场结束后就被狂喷,一群人给出了 \(O(n\sqrt{n})\) 做法,甚至 \(O(n\log n)\) 都出来了……

首先发现,修改一个点时,如果把这个点看成根,其它点权期望的变化只和在根的哪个儿子的子树中有关,\(\frac{n-sz[u]}{n}d\)(选除了这个子树中的点都能经过 \(x\))。

\(O(n\sqrt{n\log n})\) 很显然,对修改的点的度数分类讨论,度数小的就是一堆子树加,度数大的就打个标记,查询的时候把大点的贡献也算上就行了。

\(O(n\sqrt{n})\) 的话,把线段树/树状数组换成分块。度数小的修改复杂度总共是 \(O(\text{块数}+\text{度数})\)(每棵子树的区间不交)。

\(O(n\log n)\) 就不用度数根号分治了。

考虑树剖(似乎很套路?),修改一个点时,只需要对重儿子和外子树区间加。

询问一个点时,发现需要再统计的就是跳重链时,从一条链跳到另外一条链时链头和父亲之间的贡献(只有这时才有轻儿子)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=300030,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
char ch=getchar();ll x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,q,el,head[maxn],to[maxn],nxt[maxn],b[maxn],dep[maxn],sz[maxn],son[maxn],fa[maxn],top[maxn],lft[maxn],rig[maxn],cnt,tag[maxn];
inline void add(int u,int v){
to[++el]=v;nxt[el]=head[u];head[u]=el;
}
inline int qpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
return ans;
}
inline void update(int p,int v){
for(int i=p;i<=n;i+=i&-i) b[i]=(b[i]+v)%mod;
}
inline void update(int l,int r,int v){
update(l,v);update(r+1,(mod-v)%mod);
}
inline int query(int p){
int s=0;
for(int i=p;i;i-=i&-i) s=(s+b[i])%mod;
return s;
}
void dfs1(int u,int f){
dep[u]=dep[fa[u]=f]+1;
sz[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int topf){
top[u]=topf;
lft[u]=++cnt;
if(son[u]) dfs2(son[u],topf);
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==fa[u] || v==son[u]) continue;
dfs2(v,v);
}
rig[u]=cnt;
}
void update_tree(int u,int d){
tag[u]=(tag[u]+d)%mod;
if(son[u]) update(lft[son[u]],rig[son[u]],1ll*(n-sz[son[u]])*d%mod);
if(u!=1){
update(1,lft[u]-1,1ll*sz[u]*d%mod);
if(rig[u]!=n) update(rig[u]+1,n,1ll*sz[u]*d%mod);
}
}
int query_tree(int u){
int s=(1ll*n*tag[u]+query(lft[u]))%mod;
while(u){
s=(s+1ll*tag[fa[top[u]]]*(n-sz[top[u]]))%mod;
u=fa[top[u]];
}
return s;
}
int main(){
n=read();q=read();
int inv=qpow(n,mod-2);
FOR(i,1,n-1){
int u=read(),v=read();
add(u,v);add(v,u);
}
dfs1(1,0);dfs2(1,1);
while(q--){
int tp=read(),u=read();
if(tp==1) update_tree(u,1ll*inv*read()%mod);
else printf("%d\n",query_tree(u));
}
}

最新文章

  1. Qcon会议之所见所想
  2. 网站开发常用cmd命令
  3. ListView设置headerview和footerview
  4. Spring MVC 学习总结(六)——Spring+Spring MVC+MyBatis框架集成
  5. HDU 2860 并查集
  6. 饭团(1):用NABCD大法为项目奠基
  7. POJ 1847 Tram --set实现最短路SPFA
  8. makefile文件制作入门
  9. Spring REST
  10. CMD模块定义规范
  11. Docker部署JavaWeb项目实战(转)
  12. 生成器以及yield语句
  13. echarts立体效果地图-自定义区域及文字
  14. Mac下多个jdk自由切换
  15. 在exe运行界面按右键(不用按鼠标右键)
  16. linux安全分析
  17. Property&#39;s synthesized getter follows Cocoa naming convention for returning
  18. C#6.0新特性:var s = $&quot;{12}+{23}={12+23}&quot;
  19. FCC Truncate a string 解决方法
  20. android入门 — Activity生命周期

热门文章

  1. c#实现两个窗体相互传值
  2. [转载]MGR变量group_replication_primary_member
  3. Oracle通过SQL语句查看table所引用的对象(View/Function/Procedure/Trigger)
  4. Python语法速查: 7. 函数基础
  5. 如何在linux终端创建文件
  6. mysql研究跟进
  7. 腾讯云服务器ubuntu18.04部署禅道系统
  8. HTTP常见响应状态码及解释、常用请求头及解释
  9. Spring 中的观察者模式
  10. go语言的json