您的位置:首页 > 其它

树状数组(二叉索引树)(uva 1428 - Ping pong )

2014-04-11 13:22 323 查看
     个人认为树状数组在功能上是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;
}


     
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: