题意:给出n个乒乓球选手的能力值a[i],要求从其中选出三个人,一个为裁判,两个作为选手,且裁判的能力位于两者之间,位置也位于两者之间,问有多少种方法,类似这样的题,要抽象成数学模型那就应该确定其中一个的位置来找其他两个的数学关系,不妨我们先确定裁判的位置,假设裁判位置为i,那么就得从1~i-1和i+1~n中选两个能力值分别位于裁判两边的选手,那么在1~i-1中假设有c[i](i表示裁判位置)个选手能力低于裁判,那么就应该有(i-1-c[i])个能力高于裁判,而另一边也是如此以数组d[i]记,最后就是变成一个组合问题,答案就是c[i]*(n-i-d[i])+d[i]*(i-1-c[i]);那么后面只需要确定c[i]与d[i]就好,例如c[i]表示的是1-i-1选手中能力值小于裁判a[i]的有哪些,那么正如训练指南里面所说,可以用x[j]来表示能力值为j的人是否存在想x[j]=1代表存在,x[j]=0代表不存在,那么每次计算ai-1以下的能力值所有存在者的个数就行,每次就变成修改某个点的值并统计前缀和的问题,这样便可以使用树状数组来解决,数学模型也就抽象出来了,修改区间点,然后更新算前缀和。
#include<stdio.h> #include<stdlib.h> #include<vector> #include<algorithm> using namespace std; const int maxn=20005; int c[maxn],d[maxn],a[maxn]; int lowbit(int x){ return x&(-x); }//按位与求lowbit位 struct bittree{ int size; vector<int> C; void clear(){ fill(C.begin(),C.end(),0); } int sum(int x){//标准树状数组的求和函数 int ret=0; while(x>0){ ret+=C[x]; x-=lowbit(x); } return ret; } void add(int x,int d){//标准树状数组的改变单个节点值的操作 while(x<=size){ C[x]+=d; x+=lowbit(x); } } }; bittree tree; int main(){ int t,n; scanf("%d",&t); while(t--){ scanf("%d",&n); int maxsize=0; for(int i=0;i<n;i++){ scanf("%d",&a[i]); maxsize=max(maxsize,a[i]); } tree.C.resize(maxsize);//如果要像数组一样使用的话得先把空间声请了,要不然只能挨个压入容器 tree.size=maxsize; tree.clear(); for(int i=0;i<n;i++){ tree.add(a[i],1); c[i]=tree.sum(a[i]-1); } tree.clear(); for(int i=n-1;i>=0;i--){ tree.add(a[i],1); d[i]=tree.sum(a[i]-1); } long long ans=0; for(int i=0;i<n;i++)ans+=(long long)c[i]*(n-d[i]-i-1)+(long long)d[i]*(i+1-c[i]-1); printf("%lld\n",ans); } }