#根号分治,分块,dfs序#洛谷 7710 [Ynoi2077] stdmxeypz
分析
首先把距离变成深度,用dfs序转成区间问题,考虑分块,散块直接改
问题是整块,如果模数比较大,可以以深度为第一维下标差分标记,这样查询时就可以前缀和知道答案
如果模数比较小,那么给该块打标记,查询时枚举模数即可,然后块长取1000,模数阈值取300,就能尽量减少时间
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=300011,M=311; struct node{int y,next;}e[N];
int dfn[N],nfd[N],siz[N],tot,n,Q,bl,Bl,pos[N],a[N],as[N],dep[N],S[N][M],s[M][M][M];
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
void dfs(int x){
dfn[x]=++tot,nfd[tot]=x,siz[x]=1;
for (int i=as[x];i;i=e[i].next){
dep[e[i].y]=dep[x]+1;
dfs(e[i].y),siz[x]+=siz[e[i].y];
}
}
void update(int x,int p,int y,int z){
int l=dfn[x],r=dfn[x]+siz[x]-1;
if (pos[l]==pos[r]){
for (int i=l;i<=r;++i)
if (dep[nfd[i]]%p==y) a[nfd[i]]+=z;
}else{
for (int i=l;i<=pos[l]*bl;++i)
if (dep[nfd[i]]%p==y) a[nfd[i]]+=z;
if (p<=Bl) for (int i=pos[l]+1;i<pos[r];++i) s[i][p][y]+=z;
else for (int i=y;i<=n;i+=p) S[i][pos[l]+1]+=z,S[i][pos[r]]-=z;
for (int i=r;i>(pos[r]-1)*bl;--i)
if (dep[nfd[i]]%p==y) a[nfd[i]]+=z;
}
}
int main(){
n=iut(),Q=iut(),bl=1000,Bl=300;
for (int i=1;i<=n;++i) pos[i]=(i-1)/bl+1;
for (int i=2;i<=n;++i){
int x=iut();
e[i]=(node){i,as[x]},as[x]=i;
}
dfs(1);
for (int i=1;i<=Q;++i)
if (iut()==2){
int x=iut(),ans=a[x];
for (int i=1;i<=Bl;++i) ans+=s[pos[dfn[x]]][i][dep[x]%i];
for (int i=1;i<=pos[dfn[x]];++i) ans+=S[dep[x]][i];
print(ans),putchar(10);
}else{
int x=iut(),p=iut(),y=iut(),z=iut();
update(x,p,(dep[x]+y)%p,z);
}
return 0;
}