UVALive 4329 Ping pong(线段树)

本文探讨了如何利用线段树解决比赛中选手技能排序和组织比赛的问题,通过构建两个数组并使用线段树来高效计算每名选手能够组织的比赛数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=13895
题意:一条街上住有n个乒乓选手,他们有一个技能值,现在每场比赛要3个人,两个选手,一个裁判;裁判须住在他们之间,且其技能值必须在两选手之间,求一其能组织多少种比赛。

思路:首先,每个人能组织的比赛数量为 其左边技能比他小的人的数量*右边比他大的 +左边比他大的*右边比他小的。这个容易想到。
这题有点像 hdu 的 1394 题解

建两个数组 lmin 和 rmin 分别表示选手i左边比他小的数量和右边比他小的数量。用线段树求解,结点cnt值表示在该段中插入值出现的次数,算lmin时,从左到右依次插入每个选手的val值,此时只更新线段树,然后查询0~val[i]-1,这区间有多少个数出现过,就是lmin的值(想不清楚的画个图看一下)。同理,求rmin是从右到左依次插入val[i]。


//0 KB	539 ms
#include <stdio.h>
#include <string.h>
#define LL long long
#define L(u) (u<<1)
#define R(u) (u<<1|1)
const int M = 20005;
const int N = 100005;
struct Node
{
    int l,r;
    int cnt;
} node[N<<2];
int lmin[M],rmin[M],val[M];

int min (int a,int b)
{
    return a > b ? b : a;
}
int max (int a,int b)
{
    return a > b ? a : b ;
}
void Build (int u,int left,int right)
{
    node[u].l = left,node[u].r = right;
    node[u].cnt = 0;
    if (node[u].l == node[u].r)
        return ;
    int mid = (node[u].l + node[u].r)>>1;
    Build (L(u),left,mid);
    Build (R(u),mid+1,right);
}
void Update (int u,int key)
{
    if (node[u].l == node[u].r)
    {
        node[u].cnt ++;
        return ;
    }
    int mid = (node[u].l + node[u].r)>>1;
    if (key <= mid) Update(L(u),key);
    else Update(R(u),key);
    node[u].cnt = node[L(u)].cnt + node[R(u)].cnt;
}
int Query(int u,int left,int right)
{
    if (left <= node[u].l &&node[u].r <= right)
        return node[u].cnt;
    int mid = (node[u].l + node[u].r)>>1;
    if (right <= mid) return Query(L(u),left,right);
    else if (left > mid) return Query(R(u),left,right);
    else return (Query(L(u),left,mid) + Query(R(u),mid+1,right));
}
int main ()
{
    int T,n,i;
    scanf ("%d",&T);
    while (T --)
    {
        scanf ("%d",&n);
        int m = 0;
        for (i = 1; i <= n; i ++)
        {
            scanf ("%d",&val[i]);
            m = max(m,val[i]);
        }
        Build (1,0,m);
        for (i = 1;i <= n;i ++)
        {
            Update(1,val[i]);
            lmin[i] = Query(1,0,val[i]-1);
        }
        Build (1,0,m);
        for (i = n;i >= 1;i --)
        {
            Update(1,val[i]);
            rmin[i] = Query(1,0,val[i]-1);
        }
        //for (i = 1;i <= n;i ++)
        //     printf ("%d %d\n",lmin[i],rmin[i]);
        LL ans = 0;
        for (i = 1; i <= n; i ++)
            ans += lmin[i]*(n-i-rmin[i]) + (i-1-lmin[i])*rmin[i];
        printf ("%lld\n",ans);

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值