[UVALive - 4329] Ping pong 树状数组入门

题目链接: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;}
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}

那么 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))}

显而易见,这个题就可以转化为求解 c i d i {c_i和d_i。}

由于本题对位置有要求所以无法排序,如果暴力的话,你需要 O ( n 2 ) {O(n^2)} 的时间复杂度,很明显会超时。此时可以用树状数组来维护一个以 a i {a_i} 为下标的二叉索引树x,那么x[a[i]]代表a[i]出现的次数,此时 c i = x 1 + x 2 + . . . . + x i 1 {c_i=x_1+x_2+....+x_{i-1}} ,每遇到一个 a i a_i ,就add(a[i],1)。同理 d i {d_i} 就倒着遍历计算即可。树状数组计算前缀和的时间复杂度为 O ( l o g r ) r a i {O(logr)r为a_i的上限} ,所以总体时间复杂度为 O ( n l o g r ) {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;
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/107685868