#根号分治,分块,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;
}
posted @ 2024-02-22 14:05  lemondinosaur  阅读(6)  评论(0编辑  收藏  举报