zoukankan      html  css  js  c++  java
  • 树状数组

    树状数组
    在去年就会写会用了,记得去省选的时候66.7%的分数是靠树状数组得的。
    用法1:
    最开始用它是用它求逆序对,当前已经读了i个元素,比a[i]小的有query(i)个,那么比a[i]大而且比a[i]先读入的有i-query(a[i])个;

    #include<bits/stdc++.h>
    using namespace std;
    long long n,num,ans;
    long long t[500100];
    long long a[500100],b[500100];
    void in(long long &x)
    {
        char c=getchar();x=0;
        while(c<'0'||c>'9')c=getchar();
        while(c<='9'&&c>='0')x=x*10+c-'0',c=getchar();
    }
    
    void modify(long long k)
    {
        for(;k<=n;k+=(-k)&k)
        t[k]+=1;
    }
    
    long long getnum(long long k)
    {
        long long cnt=0;
        for(;k>0;k-=k&(-k))
        cnt+=t[k];
        return cnt;
    }
    
    int main()
    {
      in(n);
      for(long long i=1;i<=n;i++)
      {
          in(a[i]);
          b[i]=a[i];
      }
      sort(b+1,b+n+1);
      unique(b+1,b+n+1);
      for(long long i=1;i<=n;i++)
       a[i]=lower_bound(b+1,b+n+1,a[i])-b;
      
      for(long long i=1;i<=n;i++)
      {
          modify(a[i]);
          ans+=i-getnum(a[i]);
      }
      cout<<ans;
      return 0;
    }

    用法2:
    单点修改,区间求和

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<ctime>
    #include<cstring>
    #define inf 2147483647
    #define For(i,a,b) for(register long long i=a;i<=b;i++)
    #define p(a) putchar(a)
    #define g() getchar()
    //by war
    //2017.10.13
    using namespace std;
    long long t[1000000];
    long long a[1000000];
    long long n,m;
    long long x,y;
    void in(long long &x)
    {
        long long y=1;
        char c=g();x=0;
        while(c<'0'||c>'9')
        {
        if(c=='-')
        y=-1;
        c=g();
        }
        while(c<='9'&&c>='0')x=x*10+c-'0',c=g();
        x*=y;
    }
    void o(long long x)
    {
        if(x<0)
        {
            p('-');
            x=-x;
        }
        if(x>9)o(x/10);
        p(x%10+'0');
    }
    
    void modify(long long k,long long change)
    {
        for(;k<=n;k+=k&(-k))
        t[k]+=change;
    }
    
    long long gn(long long k)
    {
        long long cnt=0;
        for(;k>0;k-=(-k)&k)
        cnt+=t[k];
        return cnt;
    }
    
    int main()
    {
        in(n),in(m);
        For(i,1,n)
        {
        in(a[i]);
        modify(i,a[i]);    
        }
        For(i,1,m)
        {
        in(x);
        if(x==1)
        {
            in(x),in(y);
            modify(x,y);
        }
        else
        {
            in(x),in(y);
            o(gn(y)-gn(x-1)),p('
    ');
        }    
        }
         return 0;
    }

    用法3:区间修改,单点查询
    这个用到了差分的思想。
    有点像数列的裂项相消,
    a[n]==(a[n]-a[n-1])+(a[n-1]-a[n-2])+...+(a[0]-0);
    比如n==5
    原数列: 1 2 3 4 5
    差分数列:1 1 1 1 1
    l==2,r==4,这一段区间加上2
    则序列变成了
    1 4 5 6 5
    1 3 1 1 -1
    显然,实际上就是a[l]+=k,a[r+1]-=k;
    单点查询就是求前缀和。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<ctime>
    #include<cstring>
    #define inf 2147483647
    #define For(i,a,b) for(register int i=a;i<=b;i++)
    #define p(a) putchar(a)
    #define g() getchar()
    
    using namespace std;
    int n,m,x,y,l,r,pre;
    int t[500010]; 
    inline void in(int &x)
    {
        int y=1;
        char c=g();x=0;
        while(c<'0'||c>'9')
        {
        if(c=='-')
        y=-1;
        c=g();
        }
        while(c<='9'&&c>='0')x=x*10+c-'0',c=g();
        x*=y;
    }
    inline void o(int x)
    {
        if(x<0)
        {
            p('-');
            x=-x;
        }
        if(x>9)o(x/10);
        p(x%10+'0');
    }
    
    inline void modify(int k,int change)
    {
        for(;k<=n;k+=(-k)&k)
        t[k]+=change;
    }
    
    inline int getnum(int k)
    {
        int ans=0;
        for(;k>0;k-=(-k)&k)
        ans+=t[k];
        return ans;
    }
    
    int main()
    {
        in(n),in(m);
        For(i,1,n)
        {
            in(x);
            modify(i,x-pre);
            pre=x;
        }
        For(i,1,m)
        {
            in(x);
            if(x==1)
            {
                in(l),in(r),in(x);
                modify(l,x);
                modify(r+1,-x);
            }
            else
            {
                in(x);
                o(getnum(x)),p('
    ');
            }
        }
         return 0;
    }

    树状数组最后一波操作:
    区间修改和区间求和
    这时候要维护两个数组
    t[]表示原来的差分数组
    前缀和:
    sum(1,n)
    =a[1]+a[2]+a[3]...+a[n]
    =t[1]+(t[1]+t[2])+...+

    a[1]+a[2]+..+a[n]
    =t[1]+t[1]+t[2]+..+(t[1]+..+t[n])
    =n*(t[1]+..+t[n])-(0*t[1]+1*t[2]+..+(n-1)*t[n])
    我令t1[i]=(i-1)*t[i];
    1 2 3 4 5
    1 1 1 1 1
    0 1 2 3 4
    l=2,r=4,区间加2
    1 4 5 6 5
    1 3 1 1 -1
    0 3 2 3 -4
    +1*2 -4*2
    5 2
    1 2 3 4 5
    1 2 4 2
    2 1 5

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<ctime>
    #include<cstring>
    #define inf 2147483647
    #define For(i,a,b) for(register long long i=a;i<=b;i++)
    #define p(a) putchar(a)
    #define g() getchar()
    //by war
    //2017.10.14
    //Ê÷×´Êý×éʵÏÖÇø¼äÐ޸ĺÍÇø¼äÇóºÍ²Ù×÷ 
    //1 l r k
    //2 l r 
    using namespace std;
    long long n,m,l,r,pre,t[100010],t1[100010],k,f,x;
    void in(long long &x)
    {
        long long y=1;
        char c=g();x=0;
        while(c<'0'||c>'9')
        {
        if(c=='-')
        y=-1;
        c=g();
        }
        while(c<='9'&&c>='0')x=x*10+c-'0',c=g();
        x*=y;
    }
    void o(long long x)
    {
        if(x<0)
        {
            p('-');
            x=-x;
        }
        if(x>9)o(x/10);
        p(x%10+'0');
    }
    void add(long long *t,long long k,long long c)
    {
        for(;k<=n;k+=(-k)&k)
        t[k]+=c;
    }
    
    long long get(long long *t,long long k)
    {
        long long ans=0;
        for(;k>0;k-=(-k)&k)
        ans+=t[k];
        return ans;
    }
    
    long long sum(long long n)
    {
        return n*get(t,n)-get(t1,n);
    }
    
    int main()
    {
        in(n),in(m);
        For(i,1,n)
        {
            in(x);
            add(t,i,x-pre);
            add(t1,i,(i-1)*(x-pre));
            pre=x;
        }
        For(i,1,m)
        {
            in(f);
            if(f==1)
            {
              in(l),in(r),in(k);
              add(t,l,k);
              add(t,r+1,-k);
              add(t1,l,(l-1)*k);
              add(t1,r+1,r*(-k));    
            }
            else
            {
                in(l),in(r);
                o(sum(r)-sum(l-1)),p('
    ');
            }
        }
         return 0;
    }
  • 相关阅读:
    mini2440 UBoot启动过程完全分析
    Linux Shell常用命令学习(1)
    S3C6410启动模式介绍
    漫画与新媒体的传播思考
    注册表修改启动项
    邮件主题是乱码的原因gbk 在utf8 显示问题
    移动 电话费 套餐
    java 异常的问题讨论
    领结的打法
    c# 字符之间的转换
  • 原文地址:https://www.cnblogs.com/war1111/p/7663793.html
Copyright © 2011-2022 走看看