题目链接:Ping pong
题意
给你n个数,你从中取3个数,要求中间的数字大小在两边数字之间。问你总共有多少种取法。
题解
这个题首先需要分析转化。
假设第i个人作为中间数
a
1
~
a
i
−
1
有
c
i
个
数
小
于
a
i
,
那
么
有
(
i
−
1
−
c
i
)
个
数
大
于
a
i
;
{a_1~a_{i-1}有c_i个数小于a_i,那么有(i-1-c_i)个数大于a_i;}
a1~ai−1有ci个数小于ai,那么有(i−1−ci)个数大于ai;
a
i
+
1
~
a
n
有
d
i
个
数
小
于
a
i
,
那
么
有
(
n
−
i
−
d
i
)
个
数
大
于
a
i
{a_{i+1}~a_n有d_i个数小于a_i,那么有(n-i-d_i)个数大于a_i}
ai+1~an有di个数小于ai,那么有(n−i−di)个数大于ai
那么 a n s = ∑ i = 1 n ( c i ∗ ( n − i − d i ) + d i ∗ ( i − 1 − c i ) ) {ans=\sum_{i=1}^n(c_i*(n-i-d_i)+d_i*(i-1-c_i))} ans=∑i=1n(ci∗(n−i−di)+di∗(i−1−ci))
显而易见,这个题就可以转化为求解 c i 和 d i 。 {c_i和d_i。} ci和di。
由于本题对位置有要求所以无法排序,如果暴力的话,你需要 O ( n 2 ) {O(n^2)} O(n2)的时间复杂度,很明显会超时。此时可以用树状数组来维护一个以 a i {a_i} ai为下标的二叉索引树x,那么x[a[i]]代表a[i]出现的次数,此时 c i = x 1 + x 2 + . . . . + x i − 1 {c_i=x_1+x_2+....+x_{i-1}} ci=x1+x2+....+xi−1,每遇到一个 a i a_i ai,就add(a[i],1)。同理 d i {d_i} di就倒着遍历计算即可。树状数组计算前缀和的时间复杂度为 O ( l o g r ) r 为 a i 的 上 限 {O(logr)r为a_i的上限} O(logr)r为ai的上限,所以总体时间复杂度为 O ( n l o g r ) {O(nlogr)} O(nlogr)。
代码
#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;
}
}