uva 1428 Ping pong (树状数组)

题意:

一条街道上有N个选手,他们要打乒乓球赛,每个人有一个自己的skill rank,且是N个不同的数。每场比赛需要一个裁判,要求这个裁判的skill rank不能高于也不能低于这两个选手,且在街上住的位置必须在两个选手之间。问可以组织多少场比赛。


开始想到了可以用树状数组处理每个人前面有多少sr(skill rank)大于他的。但是开始想法是枚举起止点开中间有多少符合要求的裁判,是N方的。看了刘汝佳白书才知道正确做法是枚举中间的裁判。


对于裁判i,假设我们知道他之前sr小于他的人数Front【i】,和之后sr大于他的人数Back【i】。

那么他能组织的比赛数量就是Front【i】*Back【i】+(i-1-Front【i】)*(N-i-Back【i】)   (左小右大和左大右小)


这样O(n)遍历一遍就行了。


求Front【i】,Back【i】的方法和求逆序数一样。

求出Front【i】后求Back【i】不需要重新排序,清空c数组后从后往前再做一遍,但因为遍历是从后到前,而查询还是从前往后的。所以做的时候把需要+1的位置ord修改为N-i-ord就可以了。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
struct node{
    int num,ord;
}G[20010];
int a[20010];
int c[20010];
int Front[20010];
int Back[20010];
int N;
int lowbit(int n){
    return n&(-n);
}

int sum(int p){
    int res=0;
    while(p<=N){
        res+=c[p];
        p+=lowbit(p);
    }
    return res;
}


void add(int p,int n){
    while(p>0){
        c[p]+=n;
        p-=lowbit(p);
    }
}

bool cmp(node a,node b){
    if(a.num==b.num) return a.ord<b.ord;
    return a.num<b.num;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&N);
        memset(c,0,sizeof(c));
        for(int i=1;i<=N;i++){
            scanf("%d",&G[i].num);
            G[i].ord=i;
        }
        sort(G+1,G+N+1,cmp);
        for(int i=1;i<=N;i++){
            Front[i]=sum(G[i].ord);
            add(G[i].ord,1);
        }
        memset(c,0,sizeof(c));
        for(int i=N;i>=1;i--){
            int pos=N-G[i].ord+1;
            Back[i]=sum(pos);
            add(pos,1);
        }
        long long res=0;
        for(int i=1;i<=N;i++){
            res+=Front[i]*Back[i];
            res+=(N-i-Back[i])*(i-1-Front[i]);
        }
        printf("%lld\n",res);
    }
    return 0;
}





你可能感兴趣的:(树状数组)