• 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年国家队候选队员罗剑桥的论文《浅谈分块思想在一类数据处理问题中的应用》,是罗原创的一道题。

    这道题的解法是分块:

    将整个区间从左往右每$lceilsqrt{n} ceil$个分成一块。

    更新:

    将$ADDquad D X$操作分成两类

    1.   $D ge lceilsqrt{n} ceil$ 的$ADD$操作,直接更新序列相应位置上元素,并更新各元素所属块由这类$ADD$操作所贡献的和,复杂度是$O(sqrt{n})$。
    2.        $D < lceil sqrt{n} ceil$ 的$ADD$操作,我们将它记录在数组$sum[1dotslceil sqrt{n} ceil -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
    ", 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
    ", SUM(r)-SUM(l-1));
            }
        }
        return 0;
    }
  • 相关阅读:
    lambda续集——1
    c++之—— lambda表达式(有个未能解决的问题等待大佬解答)——(在stack overflow找到了答案)
    交换两个变量,只使用2个变量——权当面试了解使用
    移位实现正负数原码输出
    算法导论之——插入排序
    类模板的实现与定义相分离
    类模板
    当函数模板遇到普通函数
    c++之——template模板函数
    字符转数字,数字转字符
  • 原文地址:https://www.cnblogs.com/Patt/p/5402079.html
Copyright © 2020-2023  润新知