zoukankan      html  css  js  c++  java
  • 题解 P3372 【【模板】线段树 1】

    题目

    刚刚学了树状数组的区间加法和区间求和操作,就用来水掉这题了

    本篇适合学会树状数组的人群

    前置芝士:

    普通树状数组

    差分树状数组


    【分析】

    学过树状数组的人都知道,我们对于一个数组,进行处理后,就可以在 \(O(\log n)\) 的时间内进行单点修改和区间求和

    假设对于数组 \(a_n\) ,我们用树状数组的方法处理后,就可以单点修改任意 \(a_i\) 的值,或者求 \(\displaystyle \sum_{i=l}^r a_n\)

    如果我们差分一下 \(a_n\) ,设 \(c_n=a_n-a_{n-1}\) ,那就可以使得树状数组区间修改和单点查询 \(\displaystyle a_n=\sum_{i=1}^n c_i\)


    在差分的基础下,我们如果需要求区间和,则先相同地:

    \(\displaystyle \sum_{i=l}^ra_i=\sum_{i=1}^ra_i-\sum_{i=1}^{l-1}a_i\)

    所以,在此基础上,我们只需要求解 \(\displaystyle \sum_{i=1}^k a_i\) 即可求解区间求和问题了

    我们变型这个式子:

    \(\displaystyle \sum_{i=1}^k a_i=\sum_{i=1}^k\sum_{j=1}^ic_i=\sum_{i=1}^k(k+1-i)c_i\)

    这玩意儿没办法直接求,所以我们把它分开来:

    \(\displaystyle \sum_{i=1}^k a_i=(k+1)\sum_{i=1}^k c_i-\sum_{i=1}^k ic_i\)

    这样,就能分开来,用树状数组实现求和了。

    因此,我们只需要实现 \(c_n\)\(nc_n\) 的树状数组,即可以实现区间求和

    即前者: \(c_n\) 差分树状数组的区间和,乘上 \((k+1)\) ,减去后者: \(nc_n\) 差分树状数组的区间和


    当然,我们还应考虑修改对吧

    假设修改区间 \([l,r]\) 都加上 \(a\)

    首先, \(c_n\) 的修改肯定是没问题的:\(c_l+=a,c_{r+1}+=-a\)

    分解为差分树状数组单点修改 \(c_l\)\(c_{r+1}\)

    而我们很清楚,假设 \(c_i\) 转变为 \((c_i+a)\)

    则有 \(ic_i\) 转变为 \(i(c_i+a)=ic_i+ia\)

    也就是说,\(ic_i+=ia\) ,而 \(ic_i\) 的增加对本身累加了 \(ic_i\) 的贡献都是增加 \(ia\)

    所以只要在修改 \(c_i+=a\) 的时候,顺便修改 \(ic_i+=ia\) 即可


    那本蒟蒻就放 我码风极丑的 代码了:

    #include<cstdio>
    using namespace std;
    #define f(a,b,c,d) for(register int a=b,c=d;a<=c;a++)
    #define g(a,b,c,d) for(register int a=b,c=d;a>=c;a--)
    #define LOCAL
    typedef int i32;
    typedef unsigned int u32;
    typedef long long int i64;
    typedef unsigned long long int u64;
    const i32 MAXN=1e5+10;
    typedef i64 ar[MAXN];
    //一堆条件反射的定义
    
    namespace HABIT{//读入输出优化而已,直接跳过看正文即可
        #ifdef LOCAL
            inline char gc() { return getchar(); }
        #else
            inline char gc() {
                static char s[1<<20|1]={0},*p1=s,*p2=s;
                return (p1==p2)&&(p2=(p1=s)+fread(s,1,1<<20,stdin),p1==p2)?EOF:*(p1++);
            }
        #endif
        inline i32 read(){
            register i32 ans=0;register char c=gc();register bool neg=0;
            while(c<48||c>57) neg^=!(c^'-'),c=gc();
            while(c>=48&&c<=57) ans=(ans<<3)+(ans<<1)+(c^48),c=gc();
            return neg?-ans:ans;
        }
    
        char Output_Ans[1<<20|1],*Output_Cur=Output_Ans;
        inline void output() { Output_Cur-=fwrite(Output_Ans,1,Output_Cur-Output_Ans,stdout); }
        inline void print(char c){ if(Output_Cur-Output_Ans+1>>20) output(); *(Output_Cur++)=c; }
        inline void print(char *s) { while(*s) print( *(s++) ); }
        inline void print(u64 x){
            if(!x) { print('0'); return ; }
            char buf[30]={0},*p=buf+28;
            while(x) *(p--)=x%10+48,x/=10;
            print(p+1);
        }
        inline void print(i64 x){ if(x<0) print('-'),x=-x; print( (u64)x ); }
    }
    using namespace HABIT;
    
    //正文开始
    i32 d_N,d_M;
    ar ar_d_iC,ar_d_C;
    
    inline void add(i32 pos,i64 a){
        const i32 ia=a*pos;
        for(i32 i=pos;i<=d_N;i+=(i&(-i)))//树状数组基操,不停加 lowbit(i)
            ar_d_iC[i]+=ia,ar_d_C[i]+=a;//ici+=ia,ci+=a
    }
    inline i64 sum(i32 pos){//(pos+1)*求和 ci -求和 ici
        i64 d_Ans=0;
        for(i32 i=pos;i;i-=(i&(-i))) d_Ans+=ar_d_C[i];
        d_Ans*=(pos+1);
        for(i32 i=pos;i;i-=(i&(-i))) d_Ans-=ar_d_iC[i];
        return d_Ans;
    }
    
    inline void update(i32 h,i32 t,i64 a) { add(h,a); add(t+1,-a); }
    inline i64 query(i64 h,i64 t) { return sum(t)-sum(h-1); }
    
    int main(){
        d_N=read(),d_M=read();
        f(i,1,I,d_N) update(i,i,read());
        f(i,1,I,d_M)
            if(read()==1){
                i32 h=read(),t=read();
                update(h,t,read());
            }
            else{
                i32 h=read(),t=read();
                print(query(h,t)),print('\n');
            }
        output();
        return 0;
    }
    

    最后安利以下 本蒟蒻的博客

  • 相关阅读:
    Java 项目运用个人看法(简写)
    windows 搭建Solr连接数据库
    总结2016年,计划2017
    如何解决,自己认为特别难的问题?(文摘)
    spring -quartz 定时任务多任务配置
    (转) java Timer 定时每天凌晨1点执行任务
    spring多数据源切换,写入报错的问题
    如何合理和有效的进行数据库设计
    Main方法定点执行线程任务
    莫辜负当下,莫悔恨过去,莫打扰错过的人
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/12246684.html
Copyright © 2011-2022 走看看