题意:

给定序列 $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\n",output[i]);
return 0;
}

  

05-14 12:25