个人认为树状数组在功能上是RMQ算法的升级版。
树状数组支持以下两种操作:
Add(x,d) : 让Ax增加d;
Query(L,R): 计算A(l)+A(l+1)+A(l+2)+....+A(R);
首先,我们定义lowbit(x)为x的二进制表达式中最右边的1所对应的数值(不是序列号)。比如5的二进制是101,lowbit(5)=1;6的二进制是110,lowbit(6)=2。
如何计算lowbit呢?lowbit(x)=x&(-x);因为在计算机中负数是正数按位取反加1得到的。
变成一棵树:(叫做树状数组的原因吧<_<)
红色节点是我们要存储的,红色节点存储的是红色节点加上前面蓝色节点的和。
比如c8代表的是a1-a8的和,c12代表的是a9-a12的和。(c为辅助数组,a为存储具体数值的数组)
并且每层的lowbit的值是相同的。(觉得网络上的图意思表达的不明显,自己不会用数学绘图软件T-T,就用excel自己做了个,不太好看见谅。)
ci=a(i-lowbit(i)+1)+a(i-lowbit(i)+2)+...a(i);
有了数组c后如何计算前缀和Si呢?顺着节点i往左走,边走边“爬”(注意并不一定沿着树爬),把沿途的ci加起来就行了,如下图计算s11:
修改一个ai,需要更新哪些ci呢?从ci向右走,边走边“爬”,如下图修改a3:
我的模板:
int lowbit(int x){
return x&(-x);
}
int sum(int x){
int ret=0;
while (x>0) {
ret+=C[x];x-=lowbit(x);
}
return ret;
}
int add(int x,int d){
while (x<=n){
C[x]+=d;x+=lowbit(x);
}
return 0;
}
int query(int L,int R){
return sum(R)-sum(L-1);
}
练习题可以做uva 1428 - Ping pong
考虑第i个人当裁判的情况。加上a1到a(i-1)中有ci个比ai小,那么有(i-1)-ci个比ai大;同理,假设a(i+1)到a(n)有di个比ai小,那么就有(n-i)-di个比ai大。此时就有ci(n-i-di)+(i-ci-1)di种比赛。问题就变成求ci和di了。
ok,ci可以这样求,用辅助数组X[j]表示到目前为止,有几个ai与j相等,则ci就是X[1]...X[ai-1]的前缀和了。
时间复杂度为O(n);
我的代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn =20000;
using namespace std;
int C[200009],a[maxn],d[maxn],b[maxn],n,maxd;
int lowbit(int x){
return x&(-x);
}
int sum(int x){
int ret=0;
while (x>0) {
ret+=C[x];x-=lowbit(x);
}
return ret;
}
int add(int x,int d){
//printf("%d\n",maxd);
while (x<=maxd){
C[x]+=d;x+=lowbit(x);
}
return 0;
}
int main (){
int T;scanf("%d",&T);
while (T--){
memset(C,0,sizeof(C));
scanf("%d",&n);maxd=0;
for (int i=0;i<n;i++){
scanf("%d",&a[i]);
maxd=max(maxd,a[i]);
}
for (int i=0;i<n;i++){
d[i]=sum(a[i]-1);
add(a[i],1);
}
memset(C,0,sizeof(C));
for (int i=n-1;i>=0;i--){
b[i]=sum(a[i]-1);
add(a[i],1);
}
long long ans=0;
for (int i=0;i<n;i++){
ans+=d[i]*(n-i-1-b[i])+(i-d[i])*b[i];
}
printf("%lld\n",ans);
}
return 0;
}
如果每个人都能力值不唯一的话(>_>我没有看错题),可以用下面的代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn =20000;
using namespace std;
int C[200009],C1[200009],a[maxn],d[maxn],b[maxn],d1[maxn],b1[maxn],n,maxd;
int lowbit(int x){
return x&(-x);
}
int sum(int x){
int ret=0;
while (x>0) {
ret+=C[x];x-=lowbit(x);
}
return ret;
}
int add(int x,int d){
while (x<=maxd){
C[x]+=d;x+=lowbit(x);
}
return 0;
}
int sum1(int x){
int ret=0;
while (x<maxd+1) {
ret+=C1[x];x+=lowbit(x);
}
return ret;
}
int add1(int x,int d){
while (x>=1){
C1[x]+=d;x-=lowbit(x);
}
return 0;
}
int main (){
int T;scanf("%d",&T);
while (T--){
memset(C,0,sizeof(C));
memset(C1,0,sizeof(C1));
scanf("%d",&n);maxd=0;
for (int i=0;i<n;i++){
scanf("%d",&a[i]);
maxd=max(maxd,a[i]);
}
for (int i=0;i<n;i++){
add(a[i],1);
add1(a[i],1);
d[i]=sum(a[i]-1);
d1[i]=sum1(a[i]+1);
}
memset(C,0,sizeof(C));
memset(C1,0,sizeof(C1));
for (int i=n-1;i>=0;i--){
add(a[i],1);
add1(a[i],1);
b[i]=sum(a[i]-1);
b1[i]=sum1(a[i]+1);
}
long long ans=0;
for (int i=0;i<n;i++){
//printf("%d\n",i+1);
//printf("左小%d 右小%d\n",d[i],b[i]);
//printf("左大%d 右大%d\n",d1[i],b1[i]);
ans+=d[i]*b1[i]+d1[i]*b[i];
}
printf("%lld\n",ans);
}
return 0;
}