树状数组
- 作用
树状数组可以维护一段区间比如 [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)∗|Xi−Xj| m a x ( V i , V j ) ∗ | X i − X j | 的音量,其中 Vi V i 和 Vj V j 分别是第i 头和第j 头奶牛的听力。假设每对奶牛之间同时都在说话,请计算所有奶牛产生的音量之和是多少。
数据范围
1≤N≤20000 1 ≤ N ≤ 20000
1≤Vi≤20000;1≤Xi≤20000 1 ≤ V i ≤ 20000 ; 1 ≤ X i ≤ 20000分析
f,g f , g 两个树状数组分别维护 1……i 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;
}