树状数组感觉就是快速求前缀和的数据结构。
以下记录本人遇到的各种树状数组模板题
单点修改,区间查询
#include<stdio.h>
#define For(a,b,c) for(int a = b; a <= c; a++)
int tree[1000005];
int N, M;
int lowbit(int x)
{
return x&(-x);
}
int Query(int pos)
{
int sum = 0;
for( ; pos; pos -= lowbit(pos)) sum += tree[pos];
return sum;
}
void add(int pos, int num)
{
for( ; pos <= N; pos += lowbit(pos)) tree[pos] += num;
}
int main()
{
int a, b;
scanf("%d%d",&N,&M);
For(i,1,N)
{
scanf("%d",&a);
add(i,a);
}
char s[10];
while(M--)
{
scanf("%s %d %d",&s,&a,&b);
if(s[0] == 'Q') printf("%d\n",Query(b)-Query(a-1));
else add(a,b);
}
return 0;
}
区间修改,单点查询
题目链接 NYOJ 119 士兵杀敌(四)
#include<stdio.h>
long long tree[1000005];
char s[10];
long long N;
long long lowbit(long long x)
{
return x&(-x);
}
void Add(long long pos, long long num)
{
for(; pos <= N; pos += lowbit(pos)) tree[pos] += num;
}
long long Query(long long pos)
{
long long ans = 0;
for(; pos; pos -= lowbit(pos)) ans += tree[pos];
return ans;
}
int main()
{
long long T, l, r;
long long num;
scanf("%lld%lld",&T,&N);
while(T--)
{
scanf("%s",s);
if(s[0] == 'A')
{
scanf("%lld%lld%lld",&l,&r,&num);
Add(l,num);
Add(r+1,-num);
}
else
{
scanf("%lld",&l);
printf("%lld\n",Query(l));
}
}
return 0;
}
区间修改,区间查询
存储内容发生些变化,区间修改还是和上面的相同
#include<stdio.h>
#define For(a,b,c) for(int a = b; a <= c; a++)
#define ll long long
ll N, M;
ll c1[100005], c2[100005];
ll lowbit(ll x)
{
return x&(-x);
}
void Add(ll *v, ll pos, ll num)
{
for(; pos <= N; pos += lowbit(pos)) v[pos] += num;
}
ll Prefix_sum(ll *v, ll pos)
{
ll ans = 0;
for(; pos; pos-=lowbit(pos)) ans += v[pos];
return ans;
}
ll sum(ll x)
{
return x*Prefix_sum(c1,x) - Prefix_sum(c2,x);
}
ll Query(ll l, ll r)
{
return sum(r) - sum(l-1);
}
int main()
{
scanf("%lld %lld",&N,&M);
ll last = 0, a;
For(i,1,N)
{
scanf("%lld",&a);
Add(c1,i,a-last);
Add(c2,i,(i-1)*(a-last));
last = a;
// Add(c1,i+1,a);
// Add(c2,i+1,-i*a);
}
int flag;
ll l, r;
while(M--)
{
scanf("%d",&flag);
if(flag-1)
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",Query(l,r));
}
else
{
scanf("%lld%lld%lld",&l,&r,&a);
Add(c1,l,a);
Add(c1,r+1,-a);
Add(c2,l,(l-1)*a);
Add(c2,r+1,-r*a);
}
}
return 0;
}
求逆序对个数
按左边界从小到大排序,如果左边界相等按右边界从小到大(右边界从小到大排,是防止多加左边界相同时右边界小的个数)
排完序后,按顺序找每个点右边界比它小的个数。
暴力点的按右边桶排序,每次找前面有多少个。所以可以根据这个,使用树状数组优化。O(1)+O(n)降为2O(logn)
题目链接 POJ 3067 Japan
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define For(a,b,c) for(int a = b; a <= c; a++)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
ll tree[2005];
int n, m, k;
struct Edge
{
int x, y;
}e[1000005];
bool cmp(Edge a, Edge b)
{
if(a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int pos)
{
for(; pos <= m; pos += lowbit(pos)) tree[pos]++;
}
ll Query(int pos)
{
ll sum = 0;
for(; pos; pos -= lowbit(pos)) sum += tree[pos];
return sum;
}
int main()
{
int T, cnt = 1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
For(i,1,k) scanf("%d%d",&e[i].x,&e[i].y);
sort(e+1,e+1+k,cmp);
mem(tree,0);
ll ans = 0;
For(i,1,k)
{
ans += i - Query(e[i].y) - 1;
update(e[i].y);
}
printf("Test case %d: %lld\n",cnt++,ans);
}
return 0;
}