1. 程式人生 > >HDU 2492 Ping pong(樹狀陣列)

HDU 2492 Ping pong(樹狀陣列)

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5575 Accepted Submission(s): 2085

Problem Description
N(3<=N<=20000) 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(1<=T<=20), indicating the number of test cases, followed by T lines each of which describes a test case.

Every test case consists of N + 1 integers. The first integer is N, the number of players. Then N distinct integers a1, a2 … aN follow, indicating the skill rank of each player, in the order of west to east. (1 <= ai <= 100000, i = 1 … N).

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


Source
2008 Asia Regional Beijing

題意:有t組資料,每行資料的第一個數 n 表示有n個人,每個位置上的資料代表選手的技能值,現在要三個人組隊,按照位置的順序,三個人中間的人是裁判,兩邊的選手,裁判的技能值要跟位置一樣位於兩位選手之間。
解題思路:以自己為裁判,然後找左邊有多少人比較小,再找右邊有多少人比自己大,然後兩個數相乘就是以他為裁判的比賽場數,當然還有相反的,找右邊比自己小的,左邊比自己大的,再相乘相加。這樣子就變成了:和找逆序數、順序數差不多了,找左邊(右邊)比自己大(小)有多少個數,用樹狀陣列最好最快!所以兩個for迴圈就全部找到,然後相乘相加。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int ma=100005;
int x[ma],xx[ma],y[ma],yy[ma],a[ma],b[ma],s[ma];
int lo(int x)
{
    return x&(-x);
}
int sum(int e)
{
    int sum=0;
    while(e>0)
    {
        sum+=b[e];
        e-=lo(e);
    }
    return sum;
}
void update(int pos,int num)
{
    while(pos<=ma)
    {
        b[pos]+=num;
        pos+=lo(pos);
    }
}
int main()
{
    int t,n,flag;
    long long int summ;
    scanf("%d",&t);
    while(t--)
    {
        summ=0;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++)//求左邊比該點技能值大的數的個數,小的數的個數 
        {

            x[i]=sum(a[i]);//輸入的i個數中 有 sum(a[i]) 個比a[i]小  
            xx[i]=i-1-x[i];//輸入的i個數中 有 i-1-sum(a[i]) 個比a[i]大  
            update(a[i],1);
        }
        memset(b,0,sizeof(b));
        int j=1;//代表現在輸入的數的個數  
        for(int i=n;i>=1;i--,j++)//求右邊比該點技能值大的數的個數,小的數的個數  
        {

            y[i]=sum(a[i]);//輸入a[i]後 輸入的那些數中有 sum(a[i]) 個比a[i]小的  
            yy[i]=j-1-y[i];//輸入a[i]後輸入的那些數中有 j-1-sum(a[i] 個比a[i]大的 
            update(a[i],1);
        }
        for(int i=1;i<=n;i++)
            summ+=xx[i]*y[i]+x[i]*yy[i];
        printf("%lld\n",summ);
    }
    return 0;
}