POJ 3928 Ping pong 线段树

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

题意:

一条街上有n个乒乓球爱好者,每个人都有一个不同的技能值 a 。每场比赛需要选3个人:1个裁判,2个选手,有一个奇怪的规定:裁判必须住在选手中间,且裁判的技能值也要在选手中间。求能有多少种比赛。

思路:
可以将裁判固定, 然后求两边分别有多少人比他大和比他小。

这是就可以通过线段树求解, 建两颗线段树分别求左边的比裁判小的和右边比裁判小的, 然后比裁判大的就可以通过那部分可以通过左右两边的总人数分别减去就可以求得。 

线段树维护的是区间能力值总和。

求单点的不同的场次的式子如下:

ans1[i]*(n-i-ans2[i])+ans2[i]*(i-1-ans1[i])

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=100005;
int t;
int n;
int a[maxn];
//两颗线段树
int tree1[maxn<<2];
int tree2[maxn<<2];
//求某点右边能力值大的人数
int ans1[maxn];
//求某点左边能力值大的人数
int ans2[maxn];
void pushup (int re,int * t)
{
    t[re]=t[re<<1]+t[re<<1|1];
}
void build (int l,int r,int re,int *t)
{
    t[re]=0;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build (l,mid,re<<1,t);
    build (mid+1,r,re<<1|1,t);
}
void update (int l,int r,int re,int *t,int loc)
{
    if(l==r)
    {
        t[re]++;
        return;
    }
    int mid=(l+r)>>1;
    if(loc<=mid)
        update (l,mid,re<<1,t,loc);
    else
        update (mid+1,r,re<<1|1,t,loc);
    pushup (re,t);
}
int query (int left,int right,int re,int l,int r,int *t)
{
    if(l>=left&&r<=right)
    {
        return t[re];
    }
    int ans=0;
    int mid=(l+r)>>1;
    if(mid>=left)
        ans+=query (left,right,re<<1,l,mid,t);
    if(mid<right)
        ans+=query (left,right,re<<1|1,mid+1,r,t);
    return ans;
}
int main()
{
    scanf("%d",&t);
    while (t--)
    {

      scanf("%d",&n);
      build (1,maxn,1,tree1);
      build (1,maxn,1,tree2);
      //正向遍历
      for (int i=1;i<=n;i++)
      {
          scanf("%d",&a[i]);
          ans1[i]=query(a[i]+1,maxn,1,1,maxn,tree1);
          update (1,maxn,1,tree1,a[i]);
      }
      //反向遍历
      for (int i=n;i>=1;i--)
      {
          ans2[i]=query(a[i]+1,maxn,1,1,maxn,tree2);
          update (1,maxn,1,tree2,a[i]);
      }
      long long int sum=0;
      //求总人数
      for (int i=1;i<=n;i++)
      {
          sum+=(ans1[i]*(n-i-ans2[i])+ans2[i]*(i-1-ans1[i]));
      }
      printf("%lld\n",sum);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41410799/article/details/81711661