题目大意
有一棵有 n n n个点且以 1 1 1为根的树,有 q q q次操作,操作分为两种类型:
- 将点 v v v子树内与它距离模 x x x等于 y y y的所有点的权值加上 z z z
- 询问点 v v v的权值
1 ≤ n , q ≤ 3 × 1 0 5 , 1 ≤ z ≤ 1000 1\leq n,q\leq 3\times 10^5,1\leq z\leq 1000 1≤n,q≤3×105,1≤z≤1000
时间限制 6000 m s 6000ms 6000ms,空间限制 256 M B 256MB 256MB。
题解
前置知识:根号分治
设点 i i i在树上的深度为 d e p i dep_i depi,则第一类操作可以看成将 v v v的子树内所有满足 d e p i % x = ( d e p v + y ) % p dep_i\%x=(dep_v+y)\%p depi%x=(depv+y)%p的点 i i i的权值加上 z z z。
我们可以求每个点在树上的 d f s dfs dfs序,那么在一个子树内的节点在 d f s dfs dfs序上是一段连续的区间,这样操作一就变成区间修改了。
我们可以用分块来维护 d f s dfs dfs序上每个点的权值。
我们考虑根据 x x x的大小来进行根号分治。在操作二时,将两个部分的答案加在一起即可得到答案。
当 x ≤ n x\leq \sqrt n x≤n时,设 m d i , j , k md_{i,j,k} mdi,j,k为第 i i i块中深度模 j j j为 k k k的点需要整体加的值,对于操作一(也就是一次区间修改),分为整块和散块处理,是 O ( n ) O(\sqrt n) O(n)的。而操作二要对每一个小于等于 n \sqrt n n的模数 j j j都要 O ( 1 ) O(1) O(1)查询,所以也是 O ( n ) O(\sqrt n) O(n)的。那么,这部分的时间复杂度为 O ( q n ) O(q\sqrt n) O(qn)。
当 x > n x>\sqrt n x>n时,设 d s i , j ds_{i,j} dsi,j表示深度为 i i i时第 j j j块上要加的值。也是分为整块和散块来处理。对于整块,枚举每个满足题意的深度。如果将每个深度的每一块都修改的话,一次操作一是 O ( n ) O(n) O(n)的,所以我们可以维护这些块的拆分,那么对每一种深度都可以 O ( 1 ) O(1) O(1)修改,是 O ( n ) O(\sqrt n) O(n)的。对于操作二,在 v v v的深度对应的块中求对应段的 d s i , j ds_{i,j} dsi,j之和即可,直接枚举并求和,也是 O ( n ) O(\sqrt n) O(n)的。那么,这部分的时间复杂度为 O ( q n ) O(q\sqrt n) O(qn)。
总时间复杂度为 O ( q n ) O(q\sqrt n) O(qn),空间复杂度为 O ( n n ) O(n\sqrt n) O(nn),这是按块数和块长为 n \sqrt n n来得到的。如果 M L E MLE MLE,可以适当改变块数和块长。
code
#include<bits/stdc++.h>
using namespace std;
const int N=300000,B=200,K=1500;
int n,Q,bl,ct=0,tot=0,d[2*N+5],l[2*N+5],r[2*N+5],dfn[N+5],s[N+5],dep[N+5],siz[N+5];
int mx,ans,hv[N+5],ds[N+5][B+5],md[K+5][B+5][B+5];
void add(int xx,int yy){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
int pos(int i){
return (i-1)/bl+1;
}
void dfs(int u,int fa){
dfn[u]=++ct;
s[ct]=u;
siz[u]=1;
dep[u]=dep[fa]+1;
mx=max(mx,dep[u]);
for(int i=r[u];i;i=l[i]){
if(d[i]==fa) continue;
dfs(d[i],u);
siz[u]+=siz[d[i]];
}
}
void pt(int dd,int l,int r,int x,int y,int z){
int vl=pos(l),vr=pos(r);
if(vl==vr){
for(int i=l;i<=r;i++){
if(dep[s[i]]%x==y) hv[i]+=z;
}
return;
}
for(int i=l;i<=vl*bl;i++){
if(dep[s[i]]%x==y) hv[i]+=z;
}
for(int i=vr*bl-bl+1;i<=r;i++){
if(dep[s[i]]%x==y) hv[i]+=z;
}
if(x<=B){
for(int i=vl+1;i<=vr-1;i++) md[i][x][y]+=z;
}
else{
for(int i=dd;i<=mx;i+=x){
ds[i][vl+1]+=z;ds[i][vr]-=z;
}
}
}
int main()
{
scanf("%d%d",&n,&Q);
bl=n/B+1;
for(int i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(1,0);
for(int i=1;i<=Q;i++){
int tp,v,x,y,z;
scanf("%d",&tp);
if(tp==1){
scanf("%d%d%d%d",&v,&x,&y,&z);
pt(dep[v]+y,dfn[v],dfn[v]+siz[v]-1,x,(y+dep[v])%x,z);
}
else{
scanf("%d",&v);
ans=hv[dfn[v]];
for(int i=1;i<=B;i++){
ans+=md[pos(dfn[v])][i][dep[v]%i];
}
for(int i=1;i<=pos(dfn[v]);i++){
ans+=ds[dep[v]][i];
}
printf("%d\n",ans);
}
}
return 0;
}