peiwenjun's blog 没有知识的荒原

P7710 [Ynoi2077] stdmxeypz 题解

题目描述

给定一棵 \(n\) 个点,以 \(1\) 为根的有根树,初始每个点权值为 \(0\)

接下来 \(m\) 次操作:

  • 1 a x y z : 给 \(a\) 子树内所有到 \(a\) 距离模 \(x\) 等于 \(y\) 的节点权值加 \(z\)
  • 2 a :查询 \(a\) 的权值。

数据范围

  • \(1\le n,m\le 3\cdot 10^5\)
  • \(1\le f_i\le i,1\le a\le n,1\le x\le n,0\le y\lt x,0\le z\lt 514\)

时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{512MB}\)

分析

\(dfs\) 序把树上问题拍成序列问题,转化为有 \(n\) 个点 \((dfn_i,dep_i)\) ,每次给横坐标 \([l,r]\) 内纵坐标模 \(x\) 等于 \(y\) 的节点权值加上 \(z\)

\(x\) 根号分治:

  • \(x\le B_1\) ,对每组 \((x,y)\) 用一个数据结构维护区间加、单点查询,查询时遍历 \(1\sim B_1\) 并将贡献累加即可。修改代价为 \(1\) ,查询代价为 \(B_1\) ,选择 \(\mathcal O(\sqrt n)-\mathcal O(1)\) 分块。
  • \(x\gt B_1\) ,对每个纵坐标开一个数据结构,修改时暴力枚举所有符合条件的纵坐标即可。修改代价为 \(\frac n{B_1}\) ,查询代价为 \(1\) ,选择 \(\mathcal O(1)-\mathcal O(\sqrt n)\) 分块。

\(B_1=\sqrt n\) ,可以得到最优时间复杂度 \(\mathcal O(m\sqrt n)\) 。但是我们要维护 \(\mathcal O(n+B_1^2)\) 个数据结构,空间显然吃不消。

设分块块长为 \(B_2\) ,散块部分暴力,数据结构只维护整块部分的贡献,这样单个数据结构只需要 \(\mathcal O(\frac n{B_2})\) 的空间。

时间复杂度 \(\mathcal O(m\cdot(B_1+B_2+\frac n{B_1}+\frac n{B_2}))\) ,空间复杂度 \(\mathcal O((n+B_1^2)\cdot\frac n{B_2})\) ,需要适当开小 \(B_1\) 、开大 \(B_2\)

于是被卡常了。

最后说一下可以卡常的点:

  • 快读。本题的读入量还是比较大的。
  • \(x\gt B_1\) 时只需枚举到最大深度。原因显然,对于特殊构造的数据用处不大,但是对于大部分数据优化还是挺明显的。

经过大量的调块长(卡评测)操作后,取 \(B_1=300,B_2=880\) 可以 \(\texttt{3.99s,497MB}\) 极限飘过。

\(\texttt{upd}\) :其实一点都不卡,换了一个时间段提交相同的代码,原本极限飘过的点( \(\ge\texttt{3.9s}\) )竟然提速了 \(40\%\sim 50\%\)\(\texttt{2s}\sim\texttt{2.5s}\) )。由此可见不同评测机的性能差距非常大,如果你被卡常了,可以考虑换个时间重新交一次,说不定有意外收获。

#include<bits/stdc++.h>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int B1=300,B2=880,maxn=3e5+5;
int n,m,cnt,mxd;
int bel[maxn],dep[maxn],dfn[maxn],id[maxn],sz[maxn],val[maxn];
vector<int> g[maxn];
int s1[B1+2][B1+2][maxn/B2+2],s2[maxn][maxn/B2+2];
char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
int read()
{
    int q=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) q=q*10+ch-'0',ch=getchar();
    return q;
}
void dfs(int u)
{
    dfn[u]=++cnt,id[cnt]=u,sz[u]=1;
    for(auto v:g[u]) dep[v]=dep[u]+1,dfs(v),sz[u]+=sz[v];
}
void work(int l,int r,int x,int y,int z)
{
    for(int i=l;i<=r;i++) if(dep[id[i]]%x==y) val[i]+=z;
}
int main()
{
    n=read(),m=read();
    for(int i=2;i<=n;i++) g[read()].push_back(i);
    dfs(1);
    for(int i=1;i<=n;i++) bel[i]=(i-1)/B2+1,mxd=max(mxd,dep[i]);
    while(m--)
    {
        int op=read(),u=read();
        if(op==1)
        {
            int x=read(),y=read(),z=read();
            int l=dfn[u],r=dfn[u]+sz[u]-1;
            y=(y+dep[u])%x;
            if(bel[l]==bel[r]) work(l,r,x,y,z);
            else
            {
                work(l,bel[l]*B2,x,y,z),work((bel[r]-1)*B2+1,r,x,y,z);
                if(x<=B1) for(int i=bel[l]+1;i<bel[r];i++) s1[x][y][i]+=z;
                else for(int i=y;i<=mxd;i+=x) s2[i][bel[l]+1]+=z,s2[i][bel[r]]-=z;
            }
        }
        else
        {
            int x=dfn[u],y=dep[u],res=val[x];
            for(int i=1;i<=B1;i++) res+=s1[i][y%i][bel[x]];
            for(int i=1;i<=bel[x];i++) res+=s2[y][i];
            printf("%d\n",res);
        }
    }
    return 0;
}

posted on 2025-08-30 18:22  peiwenjun  阅读(20)  评论(0)    收藏  举报

导航