P3697乒乓比赛
时间限制 : - MS 空间限制 : 65536 KB
评测说明 : 时限1000ms
问题描述
n个乒乓球爱好者站成一排,从左往右编号1到n。他们要组织比赛切磋技术。每个人都有一个不同的技能值,编号i个人的技能值为ai。
每场比赛需要3个人:两名选手,一名裁判。要求作为裁判的那一个乒乓球爱好者所站的位置必须在两名选手的之间,并且技能值也在两名选手的之间。
问一共能组织多少场不同的比赛。
输入格式
第一行,一个整数n。
第二行,n个空格间隔的整数,按照从左往右的顺序给出了每个乒乓球爱好者的能力值ai。
输出格式
一行,一个整数,表示所求结果
样例输入 1
3
1 2 3
样例输出 1
1
样例输入 2
5
9 1 4 3 6
样例输出 2
3
样例输入 3
7
7 10 5 4 8 1 6
样例输出 3
9
提示
3<=n<=50000
1<=ai<=200000
来源 改编自LA 4329
为什么两个人打球的技能值要一个高一个低呢…不应该找旗鼓相当的对手么
题解
这个题目要用树状数组
而且需要一点点组合的意识
分类讨论
我们讨论每个人当裁判的情况
例如
技能值 1 7 6 4 2 3 5
我们探究技能值为4的人当裁判的情况
他左边有1个比他弱的人,右边有1个比他强的人
所以这些人可以打:
1*1=1组比赛
他左边有2个比他强的人,右边有2个比他弱的人
所以这些人可以打:
2*2=4组比赛(每一个左边的比他弱的人都可以和每一个右边比他强的人打比赛)
于是,这道题目的解法出来了
以每个人当裁判为不同的情况
那么每个人当裁判能够主持的比赛数即为
左边比他弱的人数*右边比他强的人数+左边比他强的人数*右边比他弱的人数
统计方法
那么我们如何统计哪些人比他弱,哪些人比他强呢??
这里就不做探究,直接告知方法了
对于每一次输入,由于是按顺序的,我们就可以按照顺序把能力值打上标记
这样子每一次输入之后,我们都可以统计出他左边比他弱/强的人的个数
例如
人的技能值 1 7 6 4 2 3 5
假设我们输入到第四个人了
那么输入之前标记
1 2 3 4 5 6 7(这一行代表技能值)
1 1 1(这一行代表标记)
输入第四个人的技能值为4
那么我们只需要求比4低的值标记之和(=1)
就能求出,他左边有1个人比他弱
又由于此时除他之外共有3个人,那么左边比他强的人的个数=3-1=2
所以他左边比他弱的人=1
比他强的人=2
求完之后,再将他的能力值标上标记即可
那么如何求他右边比他弱/强的人的个数呢??
我们可以在输入完成之后,求出全场比他弱的人
已知全场比他弱的人有x个,他左边比他弱的人有y个
那么他右边比他弱的人就有(x-y)个
同理可得右边比他强的人
提问
①如何分类讨论比赛个数??
②如何用数状数组求出左边比他强/弱的人的个数??
附上代码
#include <iostream>
#include <cstdio>
using namespace std;
inline int input()//输入优化
{
char c=getchar();int o;
while(c>57||c<48)c=getchar();
for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
return o;
}
long long res=0;
int n,x[51234];
int C[201234],higher[51234],lower[51234],found[201234];
int lowbit(int x){return x&-x;}
int find(int x)
{
int ans=0;
for(int i=x;i;i-=lowbit(i))ans+=C[i];
return ans;
}
void add(int x){for(int i=x;i<=200000;i+=lowbit(i))C[i]+=1;}
int main()
{
n=input();
for(int i=1;i<=n;i++)
{
x[i]=input();
lower[i]=find(x[i]-1);//寻找他左边比他弱的人的个数
higher[i]=i-1-lower[i];//求他左边比他强的人的个数
add(x[i]);
}
for(int i=1;i<=n;i++)
{
if(!found[x[i]])found[x[i]]=find(x[i]-1);//记录全局比他弱的人的个数
res+=lower[i]*(n-1-found[x[i]]-higher[i])+(found[x[i]]-lower[i])*higher[i];//得解
}
printf("%lld",res);
}