qzezoj 1591 序列询问
题面传送门
看数据范围,对于 \(100\%\)的数据 \(1 \leq n \leq 10^5, 1 \leq A_i \leq 10^9, 1 \leq m \leq10^5\) ,可能是\(O(nlogn)\)或\(O(n\sqrt n)\),再看开了两秒,就知道是\(O(n\sqrt n)\)了。
这道题是一道数据分治的题目。这让我想起了这道题,同样也可以数据分治。
我们思考一下怎么数据分治。由于这道题是没有修改的,则一定可以预处理出一部分答案。我们设我们处理出\(k\)个答案,则处理出\(k\)个答案的复杂度是\(o(nk)\)的,而处理剩下的直接查询的答案的复杂度是\(O(\frac{nm}{k})\),两个合并一下得到\(O(n(k+\frac{m}{k}))\),显然\(k\)取\(\sqrt m\)最优,但这样如果\(m\)很大\(n\)很小会造成不可避免的错误,所以换成与\(m\)同阶的\(n\),\(k\)取\(\sqrt n\)。
同时我们的空间复杂度和时间复杂度一样,都是\(O(n\sqrt n)\),所以空间只能开成\(160\times 100000\),所以\(k\)还要对\(160\)取一个\(min\)
代码实现:
#include<cstdio>
#include<cmath>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
long long n,m,k,x,y,ans,f[100039][160],a[100039];
int main(){
register int i,j;
scanf("%lld",&n);
k=min(sqrt(n),159);
for(i=1;i<=n;i++) scanf("%lld",&a[i]);
for(i=1;i<=k;i++){
for(j=n;j>n-i;j--) f[j][i]=a[j];
for(j=n-i;j>=1;j--) f[j][i]=f[j+i][i]+a[j];
}
scanf("%lld",&m);
for(i=1;i<=m;i++){
scanf("%lld%lld",&x,&y);
ans=0;
if(y>k)for(j=x;j<=n;j+=y) ans+=a[j];
else ans=f[x][y];
printf("%lld\n",ans);
}
}

浙公网安备 33010602011771号