题目链接:Ping pong
题意
给你n个数,你从中取3个数,要求中间的数字大小在两边数字之间。问你总共有多少种取法。
题解
这个题首先需要分析转化。
假设第i个人作为中间数
那么
显而易见,这个题就可以转化为求解
由于本题对位置有要求所以无法排序,如果暴力的话,你需要 的时间复杂度,很明显会超时。此时可以用树状数组来维护一个以 为下标的二叉索引树x,那么x[a[i]]代表a[i]出现的次数,此时 ,每遇到一个 ,就add(a[i],1)。同理 就倒着遍历计算即可。树状数组计算前缀和的时间复杂度为 ,所以总体时间复杂度为 。
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define lowbit(x) x&(-x)
const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
const int maxm=2e5+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int a[maxn],n,c[maxn],d[maxn];
int x[maxm];
int sum(int d)
{
ll sum=0;
while(d>=1) {
sum+=x[d]; d-=lowbit(d);
}
return sum;
}
void add(int d) {
while(d<=100000) {
x[d]+=1;
d+=lowbit(d);
}
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
memset(x, 0, sizeof(x));
for(int i=1;i<=n;i++)
{
c[i]=sum(a[i]);
add(a[i]);
}
memset(x, 0, sizeof(x));
for(int i=n;i>=1;i--)
{
d[i]=sum(a[i]);
add(a[i]);
}
ll sum=0;
for(int i=1;i<=n;i++)
sum+=c[i]*(n-i-d[i])+d[i]*(i-1-c[i]);
cout << sum << endl;
}
}