【CF103D】Time to Raid Cowavans-分块+离线处理

测试地址:Time to Raid Cowavans
题目大意:维护一个长为 n(3×105) n ( ≤ 3 × 10 5 ) 的数列 A A ,要求处理m(3×105)个询问,每次询问给出一个数对 (a,b) ( a , b ) ,询问 Aa+Aa+b+Aa+2b+...+Aa+kb A a + A a + b + A a + 2 b + . . . + A a + k b 的值,其中 a+kbn a + k b ≤ n a+(k+1)b>n a + ( k + 1 ) b > n
做法:本题需要用到分块+离线处理。
直接 O(nm) O ( n m ) 的暴力肯定是会炸的,需要考虑优化。
我们发现对于 bn b ≥ n 的询问,每次暴力求值的复杂度不超过 O(n) O ( n ) ,总复杂度为 O(mn) O ( m n ) ,可以接受。
重点是处理 b<n b < n 的询问,我们可以对于每个不同的 b b 值,对所有a我们 O(n) O ( n ) 处理出题目要求的后缀和,这样就可以 O(1) O ( 1 ) 回答询问了。对于每个 b b 值都是O(n)的复杂度,而 b b 值共有n种,所以总复杂度为 O(nn) O ( n n )
但是 O(nn) O ( n n ) 的空间复杂度对于70MB的空间限制来说太大了,注意到题目没有强制在线,所以我们在一开始将所有询问对 b b 排序,然后顺次处理下来,这样空间复杂度就降为O(n),可以接受。这样我们就以 O((n+m)n) O ( ( n + m ) n ) 的复杂度解决了这一问题。
(实际上我不太清楚这个题到底是不是真正的分块思想,如果标错了请见谅)
以下是本人代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,sq,m;
ll a[300010],ans[300010],sum[300010];
struct Query
{
    int id,a,b;
}q[300010];

bool cmp(Query a,Query b)
{
    return a.b<b.b;
}

int main()
{
    scanf("%d",&n);
    sq=(int)sqrt(n);
    for(int i=1;i<=n;i++)
        scanf("%I64d",&a[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        q[i].id=i;
        scanf("%d%d",&q[i].a,&q[i].b);
    }

    q[0].b=0;
    sort(q+1,q+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
        if (q[i].b<sq)
        {
            if (q[i].b!=q[i-1].b)
            {
                for(int j=n;j>=1;j--)
                {
                    if (j>n-q[i].b) sum[j]=a[j];
                    else sum[j]=sum[j+q[i].b]+a[j];
                }
            }
            ans[q[i].id]=sum[q[i].a];
        }
        else
        {
            ans[q[i].id]=0;
            for(int j=q[i].a;j<=n;j+=q[i].b)
                ans[q[i].id]+=a[j];
        }
    }

    for(int i=1;i<=m;i++)
        printf("%I64d\n",ans[i]);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值