树状数组

树状数组
在去年就会写会用了,记得去省选的时候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('\n');
    }    
    }
     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('\n');
        }
    }
     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('\n');
        }
    }
     return 0;
}

 

posted @ 2017-10-13 23:14  WeiAR  阅读(165)  评论(0编辑  收藏  举报