题意:
给定序列 $a,m$ 次询问,每次询问给出 $t,k$. 求 $a_{t}+a_{t+k}+a_{t+2k}+.....a_{t+pk}$ 其中 $t+(p+1)k>n$
题解:
这种跳步数问题可以用根号分治来解决:
对于 $k$ 比较大的询问直接暴力跳,而对于 $k$ 较小的部分就通过预处理的手段来做.
我们现在只考虑 $k<sqrt n$ 的情况,即需要我们预处理的部分.
如果直接维护 $f[i][j]$ 表示从 $i$ 开始以 $j$ 的步伐跳到 $n$ 所能得到的点权和的话空间根本开不下.
但是询问不是强制在线的,我们可以采用离线+滚动数组的方式来处理.
具体地,我们将这个序列分块,对于块内元素维护 $(i,pos,t)$ 即在第 $i$ 个块中第 $pos$ 个位置开始以 $t$ 的步伐条的元素和.
倒着枚举询问,我们就可以将第一维 $i$ 压掉,然后倒着处理并滚动优化一下即可.
#include <bits/stdc++.h> #define M 550 #define N 300005 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,m,B; ll f[M][M],A[N],output[N]; struct query { int id,k; query(int id=0,int k=0):id(id),k(k){} }; struct Data { int id,pos; vector<query>v; }p[N]; int main() { int i,j; // setIO("input"); scanf("%d",&n); B=sqrt(n); for(i=1;i<=n;++i) { scanf("%lld",&A[i]); p[i].id=(i-1)/B+1; if(p[i].id!=p[i-1].id) p[i].pos=1; else p[i].pos=p[i-1].pos+1; } scanf("%d",&m); for(i=1;i<=m;++i) { int a,b; scanf("%d%d",&a,&b); if(b<B) p[a].v.push_back(query(i, b)); else { ll re=0; for(j=a;j<=n;j+=b) re+=A[j]; output[i]=re; } } for(i=n;i>=1;--i) { int cur=p[i].pos; for(j=1;j<B;++j) { f[p[i].pos][j]=A[i]; if(i+j<=n) { f[p[i].pos][j]+=f[p[i+j].pos][j]; } } for(j=0;j<p[i].v.size();++j) { output[p[i].v[j].id]=f[p[i].pos][p[i].v[j].k]; } } for(i=1;i<=m;++i) printf("%lld ",output[i]); return 0; }