LA4329 Ping pong 樹狀數組前綴和




bit中存到考慮a[i]爲止,區間上數字的出現的個數,爲的是回來統計[1, a[i] - 1]大小範圍的數


考慮到第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]大的數

利用乘法原理,則i當裁判有c[i] * (n - i - d[i]) + d[i] * (i - 1 - c[i])種情況

sum用來求大小在[1, a[i] - 1]的數的個數,也就是對在這個範圍內的數的個數進行一次累加和


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdlib>

using namespace std;

typedef long long ll;

ll T, n, maxn;
ll a[20005], c[20005], d[20005], bit[100005];

ll max(ll x, ll y) {
	return x < y ? y : x;
}

ll lowbit(ll x) {
	return x & (-x);
}

void add(ll x, ll y) {
	//這裏需要維護的樹狀數組的區間是從x到a[i]的最大值
	for (ll i = x; i <= maxn; i += lowbit(i)) {  //debug i <= maxn
		bit[i] += y;
	}
}

ll sum(ll x) {
	ll ans = 0;
	for (ll i = x; i > 0; i -= lowbit(i)) {
		ans += bit[i];
	}
	return ans;
}

int main()
{
	cin >> T;
	while (T--) {
		scanf("%lld", &n);
		maxn = -1;
		for (ll i = 1; i <= n; i++) {
			scanf("%lld", &a[i]);
			maxn = max(maxn, a[i]);  //debug
		}

		memset(bit, 0, sizeof(bit));
		for (ll i = 1; i <= n; i++) {
			add(a[i], 1);
			c[i] = sum(a[i] - 1); //求到i爲止出現過多少個小於a[i]的數
		}

		//反向同理求出d
		memset(bit, 0, sizeof(bit));
		for (ll i = n; i >= 1; i--) {
			add(a[i], 1);
			d[i] = sum(a[i] - 1);
		}

		ll ans = 0;
		//乘法原理求出最終結果
		for (ll i = 2; i <= n - 1; i++) {
			ans = ans + c[i] * (n - i - d[i]) + d[i] * (i - c[i] - 1);
		}
		printf("%lld\n", ans);
	}
	return 0;
}


發佈了198 篇原創文章 · 獲贊 8 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章