题意:有n个兵乓球选手,每个人都有唯一的一个技能值,每场比赛需要一个裁判,这个裁判的技能值和标号都要在两个选手之间,问能够举办的比赛的种类。
思路:对于第i个人来说,如果可以求出在他左边比他技能值小的数量ai和在他右边比技能值比他小的人得数量bi,那么就可以分别算出左右两边比他技能值大的选手的数量ci和di,则当第i个人当裁判时,就有ai*di+bi*ci种比赛种类。由于技能值的范围比较小,求ai的时候可以从左到右扫描,计算在i前比它小的技能值的数量,计算方法可以使用树状数组求ai的前缀和(也可以用线段树),计算bi与ai同理,从右向左扫描即可。不过事实证明还是树状数组比较快啊,还比较好写。
代码:
树状数组实现:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
ll C[maxn],ci[maxn],di[maxn],num[maxn];
int lowbit(int x)
{
return x&-x;
}
ll sum(int x)
{
ll ret=0;
while(x>0)
{
ret+=C[x];
x-=lowbit(x);
}
return ret;
}
void add(int x,int v,ll n)
{
while(x<=n)
{
C[x]+=v;
x+=lowbit(x);
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t,n;
cin>>t;
while(t--)
{
cin>>n;
ll res=0;
ll maxnum=0;
for(int i=1;i<=n;++i)
{
cin>>num[i];
maxnum=max(maxnum,num[i]);
}
memset(C,0,sizeof(C));
for(int i=1;i<=n;++i)
{
add(num[i],1,maxnum);
ci[i]=sum(num[i])-1;
}
memset(C,0,sizeof(C));
for(int i=n;i>=1;--i)
{
add(num[i],1,maxnum);
di[i]=sum(num[i])-1;
}
for(int i=1;i<=n;++i)
{
res+=ci[i]*(n-i-di[i])+di[i]*(i-1-ci[i]);
}
cout<<res<<endl;
}
return 0;
}
线段树实现:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int N=100000;
const int maxn=(100000+10)<<1;
const int maxm=20000+10;
int C[maxn<<1],num[maxm],ci[maxm],di[maxm];
void PushUp(int rt)
{
C[rt]=C[rt<<1]+C[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l==r)
{
C[rt]=0;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
PushUp(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
if(l>=L&&r<=R)
{
return C[rt];
}
int sum=0;
int m=(l+r)>>1;
if(m>=L)
sum+=Query(L,R,l,m,rt<<1);
if(m<R)
sum+=Query(L,R,m+1,r,rt<<1|1);
return sum;
}
void Update(int p,int l,int r,int rt)
{
if(l==r)
{
C[rt]+=1;
return;
}
int m=(l+r)>>1;
if(m>=p)
Update(p,l,m,rt<<1);
else
Update(p,m+1,r,rt<<1|1);
PushUp(rt);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t,n;
cin>>t;
while(t--)
{
cin>>n;
build(1,N,1);
ll res=0;
for(int i=1;i<=n;++i)
{
cin>>num[i];
Update(num[i],1,N,1);
//cout<<Query(1,num[i],1,N,1)<<endl;
ci[i]=Query(1,num[i],1,N,1)-1;
}
build(1,N,1);
for(int i=n;i>=1;--i)
{
Update(num[i],1,N,1);
di[i]=Query(1,num[i],1,N,1)-1;
}
for(int i=1;i<=n;++i)
{
res+=ci[i]*(n-i-di[i])+di[i]*(i-1-ci[i]);
}
cout<<res<<endl;
}
return 0;
}