DLUTOJ #1306 Segment Tree?
Description
有一个N个整数的序列(每个数的初值为0)。每个数都是整数。你有M次操作。操作有两种类型:
——Add Di Xi 从第一个数开始每隔Di 个位置增加Xi
——Query Li Ri 回答当前序列Li项到Ri项的和
Input
两个数N和M,输入到文件结尾。以下M行每行的输入两种操作形式的一种。(1 <= N, M, Di, Xi, Li, Ri <= 100000, Li <= Ri )
Output
对于每组数据,输出每组的询问的结果。
Sample Input
4 4
Query 2 3
Add 1 1
Query 2 3
Query 1 4
Sample Output
0
2
4
HINT
Source
这道题出自2013年国家队候选队员罗剑桥的论文《浅谈分块思想在一类数据处理问题中的应用》,是罗原创的一道题。
这道题的解法是分块:
将整个区间从左往右每$\lceil\sqrt{n}\rceil$个分成一块。
更新:
将$ADD\quad D \ X$操作分成两类
- $D \ge \lceil\sqrt{n} \rceil$ 的$ADD$操作,直接更新序列相应位置上元素,并更新各元素所属块由这类$ADD$操作所贡献的和,复杂度是$O(\sqrt{n})$。
- $D < \lceil \sqrt{n} \rceil$ 的$ADD$操作,我们将它记录在数组$sum[1\dots\lceil \sqrt{n} \rceil -1]$上:即对于$ADD \quad D \ X$,将$X$累加在$sum[D]$上,复杂度是$O(1)$。
查询:
对于查询区间$[L, R]$,分别查询上述两类$ADD$操作对$[L, R]$的贡献,相加即是答案。
Implementation:
这题我调了很长时间,先贴上第一版(有bug)代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N(1e5+5); int n, m, b; LL bucket[N], sum[N], a[N]; char op[10]; inline int ID(int x, int b){ //x>=0 return x? (x-1)/b+1: 0; } int main(){ for(int T=0; ~scanf("%d%d", &n, &m); T++){ if(T==40) for(;;); memset(a, 0, sizeof(a)); memset(bucket, 0, sizeof(bucket)); memset(sum, 0, sizeof(sum)); b=sqrt(n); for(int d, x, l, r; m--; ){ scanf("%s", op); if(*op=='A'){ scanf("%d%d", &d, &x); if(d>=b){ for(int i=1; i<=n; i+=d) a[i]+=x, bucket[ID(i, b)]+=x; } else sum[d]+=x; } else{ scanf("%d%d", &l, &r); LL res=0; int L=ID(l-1, b)+1, R=ID(r+1, b)-1; //error-prone //////////////////////////////////////// for(int i=l; i<=b*(L-1); i++) res+=a[i]; for(int i=b*R+1; i<=r; i++) res+=a[i]; //////////////////////////////////////// for(int i=L; i<=R; i++) res+=bucket[i]; for(int i=1; i<b; i++){ res+=(ID(r, i)-ID(l-1, i))*sum[i]; } printf("%lld\n", res); } } } return 0; }
bug就在分离出来的那两行,坑暂时留着,以后填。
bug-free version
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N(1e5+5); int n, m, b, id[N]; LL bucket[N], sum[N], a[N]; char op[10]; LL SUM(int x){ LL res=0; int R=id[x+1]-1; for(int i=1; i<=R; i++) res+=bucket[i]; for(int i=R*b+1; i<=x; i++) res+=a[i]; for(int i=1; i<b; i++) res+=((x-1)/i+1)*sum[i]; return res; } int main(){ for(; ~scanf("%d%d", &n, &m); ){ memset(a, 0, sizeof(a)); memset(bucket, 0, sizeof(bucket)); memset(sum, 0, sizeof(sum)); b=sqrt(n); for(int i=1; i<=n+1; i++) id[i]=(i-1)/b+1; for(int d, x, l, r; m--; ){ scanf("%s", op); if(*op=='A'){ scanf("%d%d", &d, &x); if(d>=b) for(int i=1; i<=n; i+=d) a[i]+=x, bucket[id[i]]+=x; else sum[d]+=x; } else scanf("%d%d", &l, &r), printf("%lld\n", SUM(r)-SUM(l-1)); } } return 0; }