树状数组的复习和总结

树状数组

  • 作用
    树状数组可以维护一段区间比如 [1,n] [ 1 , n ] ,支持单点修改和查询 [1,l] [ 1 , l ] 的前缀和。
    修改的操作以及查询和的操作时间复杂度为 O(logN) O ( l o g N )
  • 与线段树相比较
    优点:代码量相对于线段树来讲较小,维护相对较简单,且常数较小。
    缺点:维护的信息量少,只能支持单点修改,无法进行区间修改。

例题

  • LOI 1637三元上升子序列

    题目描述

    Erwin最近对一种叫”thair”的东西巨感兴趣……
    在含有n个整数的序列 a1,a2......an a 1 , a 2 . . . . . . a n 中,三个数被称作”thair”当且仅当 i<j<k i < j < k ai<aj<ak a i < a j < a k ,求一个序列中”thair”的个数。

    数据范围

    60%的数据n<=2000
    100%的数据n<=30000
    大数据随机生成
    0<=a[i]<=maxlongint

    分析

    因为数据 ai a i 有些大,所以先对原数据进行离散化,之后用两个树状数组来维护,一个维护前面有多少比当前位小的,另一个维护后面比当前位大的。

#include <bits/stdc++.h>
#define rep( i , l , r ) for( int i = (l) ; i <= (r) ; ++i )
#define per( i , r , l ) for( int i = (r) ; i >= (l) ; --i )
#define erep( i , u ) for( int i = head[(u)] ; ~i ; i = e[i].nxt )
using namespace std;
int _read(){
    char ch = getchar();
    int x = 0 , f = 1 ;
    while( !isdigit( ch ) )
           if( ch == '-' ) f = -1 , ch = getchar();
           else ch = getchar();
    while( isdigit( ch ) )
           x = (ch  - '0') + x * 10 , ch =  getchar();
    return x * f;
}
const int maxn = 30000 + 5;
int N;
typedef long long ll;
struct BIT{
    int key[maxn];
    inline int lowbit( int x ){ return x & (-x); }
    void add( int x ){ for( ; x <= N ; x += lowbit(x) ) key[x]++; }
    void del( int x ){ for( ; x <= N ; x += lowbit(x) ) key[x]--; }
    ll q( int x ){
        ll t = 0;
        for( ; x ; x -= lowbit(x) ) t += (ll)key[x];
        return t;
    }
    ll query( int l , int r ){ return q(r) - q(l - 1); }
} f , g;
int A[maxn] , B[maxn];
int main(){
    N = _read();
    ll ans = 0;
    rep( i , 1 , N ) A[i] = B[i] = _read();
    sort( B + 1 , B + N + 1 );
    int *tail = unique( B + 1 , B + N + 1 );
    rep( i , 1 , N ) A[i] = lower_bound( B + 1 , tail , A[i] ) - B;
    rep( i , 1 , N ) g.add( A[i] );
    rep( i , 1 , N ){
        g.del( A[i] );
        ans += (ll)( f.query( 1 , A[i] - 1 ) * g.query( A[i] + 1 , N ) );
        f.add( A[i] );
    }
    cout << ans << endl;
    return 0;
}
  • LOI 2345 奶牛集会

    题目描述

    FJ的N 头奶牛每年都会参加“哞哞大会”。哞哞大会是奶牛界的盛事。集会上的活动很多,比如堆干草,跨栅栏,摸牛仔的屁股等等。它们参加活动时会聚在一起,第i 头奶牛的坐标为Xi,没有两头奶牛的坐标是相同的。奶牛们的叫声很大,第i 头和第j 头奶牛交流,会发出 max(Vi,Vj)|XiXj| m a x ( V i , V j ) ∗ | X i − X j | 的音量,其中 Vi V i Vj V j 分别是第i 头和第j 头奶牛的听力。假设每对奶牛之间同时都在说话,请计算所有奶牛产生的音量之和是多少。

    数据范围

    1N20000 1 ≤ N ≤ 20000
    1Vi20000;1Xi20000 1 ≤ V i ≤ 20000 ; 1 ≤ X i ≤ 20000

    分析

    f,g f , g 两个树状数组分别维护 1i 1 … … i 的人数和位置之和,之后直接计算即可。

#include <bits/stdc++.h>
#define rep( i , l , r ) for( int i = (l) ; i <= (r) ; ++i )
#define per( i , r , l ) for( int i = (r) ; i >= (l) ; --i )
#define erep( i , u ) for( int i = head[(u)] ; ~i ; i = e[i].nxt )
using namespace std;
int _read(){
    char ch = getchar();
    int x = 0 , f = 1 ;
    while( !isdigit( ch ) )
           if( ch == '-' ) f = -1 , ch = getchar();
           else ch = getchar();
    while( isdigit( ch ) )
           x = (ch  - '0') + x * 10 , ch =  getchar();
    return x * f;
}
typedef long long ll;
const int maxn = 20000 + 5;
int N , M;
struct BIT{
    ll key[maxn];
    inline int lowbit( int x ) { return x & (-x); }
    void add( int x , int d ){ for( ; x <= N ; x += lowbit(x) ) key[x] += d; }
    void del( int x , int d ){ for( ; x <= N ; x += lowbit(x) ) key[x] -= d; }
    ll q( int x ){
        ll t = 0;
        for( ; x ; x -= lowbit(x) ) t += key[x];
        return t;
    }
    ll query( int l , int r ){ return q( r ) - q( l - 1 ); }
} f , g;
struct Node{
    int v , x;
} p[maxn];
inline bool cmp( Node a , Node b ){ return a.v < b.v; }
int main(){
    M = _read();
    rep( i , 1 , M ) p[i].v = _read() , N = max( N , p[i].x = _read() );
    sort( p + 1 , p + M + 1 , cmp );
    ll res = 0 , _ps = 0 , cnt = 0 , ans = 0;
    rep( i , 1 , M ){
        res += p[i].x;
        _ps = cnt = 0;
        f.add( p[i].x , 1 ) ; g.add( p[i].x , p[i].x );
        cnt = f.query( 1 , p[i].x );
        _ps = g.query( 1 , p[i].x );
        ans += ( cnt * p[i].x - _ps ) * p[i].v;
        cnt = i - cnt , _ps = res - _ps;
        ans += ( _ps - cnt * p[i].x ) * p[i].v; 
    }
    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值