其他分享
首页 > 其他分享> > Codeforces 1207F:Remainder Problem

Codeforces 1207F:Remainder Problem

作者:互联网

You are given an array a consisting of 500000 integers (numbered from 1 to 500000). Initially all elements of a are zero.

You have to process two types of queries to this array:

1 x y — increase ax by y;
2 x y — compute ∑i∈R(x,y)ai, where R(x,y) is the set of all integers from 1 to 500000 which have remainder y modulo x.
Can you process all the queries?

卡住自己两次的一道题,在我看来是个偏trick甚至有些套路的题,

由于查询和的下标不是连续的,而是每隔一个固定的单位,想不到什么太好的高级数据结构去维护,这个时候可以考虑分块。

普通的分块一般是大块维护,小块暴力,有意思的是这里恰好相反,我们注意到当x越大的时候,我们暴力做第二种操作的次数就会变少,第二种操作的复杂度是O(N/X)的,

而当x较小的时候,我们就不能暴力去跑第二种操作,而要通过之前维护的一些信息去快速出答案,

我们建立一个二维数组sum[x][y]表示当所有下标模x余y的数的和,也就是第二个操作询问的答案,考虑第一个操作会对哪些值产生变化,

假设第一个操作的下标是pos,增加的量是delta,那么会对sum[1][pos%1],sum[2][pos%2],sum[3][pos%3]……产生增加delta的变化

假设我们二维数组开成sum[MAXK][MAXK]的,那么第二种操作就是O(MAXK)的,而第二种操作在x>=MAXK的时候才需要进行,复杂度是O(N/MAXK),因此根据均值不等式,我们取MAXK=sqrt(MAXN),这样的话每次操作的复杂度就变成O(sqrt(MAXN))了,然后就做完了。

算是分块思想的一种应用吧,如果没有什么思路的时候可以尝试去考虑一下,根据根号分块,观察是不是可以存着一侧暴力一侧维护的情况,如果可以的话就可以用分块来尝试了。

银川的那道金牌题(放在一般区域赛最多是银牌题)也是分块,不过那个数学上应该比较容易看出来

底下的代码里sum[][]的两维跟上面说的调换了过来。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5;
const int k=750;
ll sum[k+5][k+5];
int a[maxn+5];
int q,op,x,y;
int main()
{
    scanf("%d",&q);
    while (q--) {
        scanf("%d%d%d",&op,&x,&y);
        if (op==1) {
            a[x]+=y;
            for (int i=1;i<k;i++)
                sum[x%i][i]+=y;
        }
        else {
            if (x>=k) {
                ll ans=0;
                for (int i=y;i<=maxn;i+=x) ans+=a[i];
                printf("%I64d\n",ans);
            }
            else printf("%I64d\n",sum[y][x]);
        }
    }
    return 0;
}

 

标签:分块,int,1207F,sum,MAXK,第二种,操作,Problem,Remainder
来源: https://www.cnblogs.com/xyw5vplus1/p/12247597.html