• 【树状数组区间修改区间求和】codevs 1082 线段树练习 3


    http://codevs.cn/problem/1082/

    【AC】

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn=2e5+2;
     5 int n;
     6 ll a[maxn];
     7 ll c1[maxn];
     8 ll c2[maxn];
     9 int lowbit(int x)
    10 {
    11     return x&-x;
    12 }
    13 void add(ll *c,int k,ll val)
    14 {
    15     while(k<=n){
    16         c[k]+=val;
    17         k+=lowbit(k);
    18     }
    19 }
    20 ll query(ll *c,int k)
    21 {
    22     ll ans=0;
    23     while(k)
    24     {
    25         ans+=c[k];
    26         k-=lowbit(k);
    27     }
    28     return ans;
    29 }
    30 ll solve(int x)
    31 {
    32     ll ans=0;
    33     ans+=x*query(c1,x);
    34     ans-=query(c2,x);
    35     return ans; 
    36 }
    37 ll solve(int x,int y)
    38 {
    39     return solve(y)-solve(x-1); 
    40 }
    41 int main()
    42 {
    43     while(~scanf("%d",&n))
    44     {
    45         a[0]=0;
    46         for(int i=1;i<=n;i++)
    47         {
    48             scanf("%I64d",&a[i]);
    49         //    cout<<a[i]<<endl;
    50             add(c1,i,a[i]-a[i-1]);
    51             add(c2,i,(i-1)*(a[i]-a[i-1]));
    52         } 
    53         int q;
    54         scanf("%d",&q);
    55         int tp;
    56         while(q--)
    57         {
    58             scanf("%d",&tp);
    59             if(tp==1)
    60             {
    61                 int x,y;ll val;
    62                 scanf("%d%d%I64d",&x,&y,&val);    
    63                 add(c1,x,val);
    64                 add(c1,y+1,-val);
    65                 add(c2,x,1ll*(x-1)*val);
    66                 add(c2,y+1,-1ll*y*val);
    67             }
    68             else
    69             {
    70                 int x,y;
    71                 scanf("%d%d",&x,&y);
    72                 ll ans=solve(x,y);
    73                 printf("%I64d
    ",ans);
    74             }
    75         }
    76     }
    77     return 0;
    78 }
    View Code

    【原理】

    原理是用了差分数组,转载自http://www.cnblogs.com/boceng/p/7222751.html

    树状数组时间复杂度为O(MlogN), 实际用的时候优于线段树,且写得少。

    神牛是引入了差分数组,要维护的差分数组ks[i] = a[i] - a[i-1]; 可以容易得到a[i] = ks[1] + ks[2] + ... + ks[i]; 即前i项和,为方便记为sigma(ks, i),已经可以看到树状数组的影子了,所以求区间和随之得到

    a[1] + a[2] + .. + a[n] = sigma(ks, 1) + sigma(ks, 2) + ... + sigma(ks, n);

      = n*ks[1] + (n-1)*ks[2] + ... + 2*ks[n-1] + 1*ks[n];

           =  n*(ks[1] + ks[2] +...+ ks[n]) - (0*ks[1] + 1*ks[2] + ... + (n-1)*ks[n]);

    所以可以得到 sum[n] =n * sigma(ks, n)  - (0*ks[1] + 1*ks[2] + ... + (n-1)*ks[n]);

    令jk[i] = (i-1) * ks[i];

    则 sum[n] = n * sigma(ks, n) - sigma(jk, n);

    之后便是构造两个树状数组;

     1 int lowbit(int k){
     2     return k & -k;
     3 }
     4 void add(int n, int *c, int k, int va){
     5     while(k <= n){
     6         c[k] += va;
     7         k += lowbit(k);
     8     }
     9 }
    10 
    11 //-------------------------------------
    12 
    13 for(i = 1; i <= n; ++i){
    14         add(n, c1, i, jk[i]-jk[i-1]);
    15         add(n, c2, i, (i-1)*(jk[i]-jk[i-1]));
    16     }
    View Code

    然后进行查询求和

     1 int sigma(int *c, int k){
     2     int sum = 0;
     3     while(k){
     4         sum += c[k];
     5         k -= lowbit(k);
     6     }
     7     return sum;
     8 }
     9 int getSum(int s, int t){
    10     return (t*sigma(c1, t)-sigma(c2, t)) - ((s-1)*sigma(c1, s-1)-sigma(c2, s-1));
    11 }
    View Code

    进行单点查询时,只需两个参数均传入该点。

    在进行区间更新的时候,神牛市通过两次维护c1,两次c2得到的,但本人推测了几种情况,都不能很好的解释这么做的原因,

    1 void update(int s, int t, int va){
    2     add(c1, s, va);
    3     add(c1, t+1, -va);
    4     add(c2, s, va*(s-1));
    5     add(c2, t+1, -va*t);
    6 }
    View Code
  • 相关阅读:
    图像处理之优化---任意半径局部直方图类算法在PC中快速实现的框架
    新的验证方式---短信验证和语言验证
    习武 之路---通背拳和苗刀!
    模式识别之Shape Context---利用Shape Context进行形状识别
    ELK 部署
    rsync实现文件备份同步
    oracle-3-子查询和常用函数
    oracle-2中commit 详解
    使用nginx绑定域名,代理gitlab
    Linux Crontab 安装使用详细说明
  • 原文地址:https://www.cnblogs.com/itcsl/p/7436388.html
Copyright © 2020-2023  润新知