DLUTOJ #1306 Segment Tree?

Description

有一个N个整数的序列(每个数的初值为0)。每个数都是整数。你有M次操作。操作有两种类型:

 

——Add  Di  Xi 从第一个数开始每隔Di 个位置增加Xi

 

——Query Li  Ri 回答当前序列Li项到Ri项的和

Input

两个数N和M输入到文件结尾。以下M行每行的输入两种操作形式的一种。(<= 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$操作分成两类

  1.   $D \ge \lceil\sqrt{n} \rceil$ 的$ADD$操作,直接更新序列相应位置上元素,并更新各元素所属块由这类$ADD$操作所贡献的和,复杂度是$O(\sqrt{n})$。
  2.        $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;
}

 

posted @ 2016-04-17 20:35  Pat  阅读(205)  评论(0编辑  收藏  举报