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