1. 程式人生 > >樹狀陣列 (uva1428)

樹狀陣列 (uva1428)

N <tex2html_verbatim_mark>(3$ \le$N$ \le$20000) <tex2html_verbatim_mark>ping pong players live along a west-east street(consider the street as a line segment). Each player has a unique skill rank. To improve their skill rank, they often compete with each other. If two players want to compete, they must choose a referee among other ping pong players and hold the game in the referee's house. For some reason, the contestants can't choose a referee whose skill rank is higher or lower than both of theirs. The contestants have to walk to the referee's house, and because they are lazy, they want to make their total walking distance no more than the distance between their houses. Of course all players live in different houses and the position of their houses are all different. If the referee or any of the two contestants is different, we call two games different. Now is the problem: how many different games can be held in this ping pong street?

Input 

The first line of the input contains an integer T <tex2html_verbatim_mark>(1$ \le$T$ \le$20) <tex2html_verbatim_mark>, indicating the number of test cases, followed by T <tex2html_verbatim_mark>lines each of which describes a test case.

Every test case consists of N + 1 <tex2html_verbatim_mark>integers. The first integer is N

 <tex2html_verbatim_mark>, the number of players. Then N <tex2html_verbatim_mark>distinct integers a1, a2...aN <tex2html_verbatim_mark>follow, indicating the skill rank of each player, in the order of west to east ( 1$ \le$ai$ \le$100000 <tex2html_verbatim_mark>, i = 1...N <tex2html_verbatim_mark>).

Output 

For each test case, output a single line contains an integer, the total number of different games.

Sample Input 

1
3 1 2 3

Sample Output 

1

題意:

題意:

        一條大街上住著n個乒乓球愛好者,他們經常組織乒乓球比賽且每個人的能力值ai都不同.每次比賽需要2個比賽者和一個裁判,他們有一個奇怪的規定:當裁判的那個人必須住在這兩個比賽者之間,且裁判的能力值也必須在這兩個人之間.問一共有多少種比賽組織方式.

        輸入:首先是T(1<=T<=20),表示例項個數.對於每個例項第一行是一個n(3<=n<=20000),然後是n個不同的整數即a1,a2…an(1<=ai<=100000),按照他們的住所位置從左到右給出每個人的能力值.

        輸出:比賽組織的總數.

分析:

        對於第i個(任意一個)裁判,他能組織多少種比賽呢?

        假設a1到ai-1有S_L[i]個能力值比ai小的,那麼有i-1-S_L[i]個能力值比ai大的.同理可得S_R[i]為從i+1到n有B[i]個人能力值比ai小.那麼i當裁判能組織的比賽種數是:S_L[i]*(n-i-S_R[i])+(i-1-S_L[i])*S_R[i]種. 我們只需要求出所有裁判的S_L[i]和S_R[i]即可求出所有比賽種數.

        如何求S_L[i]呢(即在a[i]左邊且比a[i]值小的數個數)? 我們令X[n]作為標記陣列,假設當前我們掃描到了第i個點,此時如果X[y]==1,那麼表示第i個點左邊出現了一個a[k]==y的值且k<i。那麼這時,我們只要用樹狀陣列計算X[n]陣列中區間[1, a[i]-1]的和即可(想想是不是)。 程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=20000+100;
const int MAX_NUM=100000+100;
int S_L[MAXN],S_R[MAXN];
int x[MAX_NUM];
int c[MAX_NUM];
int a[MAXN];//能力值
int max_a;
int lowbit(int i)
{
    return i&(-i);
}
int sum(int i)//求從A[1]到A[i]的和
{
    int res=0;
    while(i>0)
    {
        res += c[i];
        i-=lowbit(i);
    }
    return res;
}
void add(int i,int d)//使得C陣列的所有包含A[i]的項都加上d
{
    while(i<=max_a)//這裡max_a 是能力值的上限
    {
        c[i]+=d;
        i += lowbit(i);
    }
}
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        max_a=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            max_a=max(max_a,a[i]);
        }
 
        memset(c,0,sizeof(c));//這個for迴圈就是求s_l[i];具體思路請看下圖
        for(int i=1;i<=n;i++)
        {
            S_L[i]=sum(a[i]);
            add(a[i],1);
        }
        memset(c,0,sizeof(c));
        for(int i=n;i>=1;i--)
        {
            S_R[i]=sum(a[i]);
            add(a[i],1);
        }
        long long ans=0;
        for(int i=1;i<=n;i++)
        {
            ans += (long long)S_L[i]*(n-i-S_R[i])+(long long)(i-1-S_L[i])*S_R[i];
        }
        printf("%lld\n",ans);//注意UVA關於64位整數的格式
    }
    return 0;
}