UVA1428 Ping pong 题解

虽然用的都是树状数组, 但我的方法似乎和其他题解有点不太一样(

简化题意, 将题目转化为这个问题:

给定一个数列 \(s\), 求在\(s\)中有多少组数\((a,b,c)\)满足\(a<b<c\)\(s_a<s_b<s_c\)\(s_a>s_b>s_c\)

\(s_a<s_b<s_c\)\(s_a>s_b>s_c\)的情况分别讨论.

可以发现, 解决有多少组数\((a,b,c)\)满足\(s_a>s_b>s_c\)这个问题只要把\(s\)顺序颠倒过来再算满足\(s_a<s_b<s_c\)的数量就可以了.

而算\(s_a<s_b<s_c\)的数量, 可以先算满足\(a<b\)\(s_a<s_b\)的数量, 再算满足\(b<c\)\(s_b<s_c\)的数量(程序实践的时候可以在一起计算).这需要用到树状数组, 具体的说明可以参考这道题的题解, 这里不再赘述.

有一个地方需要注意, 就是 rank 这个名字在 C++ 的某个头文件里被用过, 但洛谷的编译器是不会触发 CE 的, 而在 UVA 中会 CE.

下面贴上代码.

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#define ll long long
#define lowbit(x) x & (-x)
using namespace std;
struct NODE {
	int val, id;
} a[20003], b[20003];
inline bool cmp (const NODE &x, const NODE &y) {
	return x.val == y.val ? x.id > y.id : x.val < y.val;
}
int n, ranks[20003], score[20003], c[20003], d[20003];
inline int query(int x, int *c) {
	int ans = 0;
	while (x)
		ans += c[x], x -= lowbit(x);
	return ans;
}
inline void add (int x, int y, int *c) {
	while (x <= n)
		c[x] += y, x += lowbit(x);
	return;
}
inline ll sovle() {
	memset(c, 0, sizeof(c));
	memset(d, 0, sizeof(d));
	memset(score, 0, sizeof(score)); 
	ll ans = 0;
	for (int i = 1; i <= n; i++)
		ranks[b[i].id] = i;		// 第 i 个数的排名 
	for (int i = 1; i <= n; i++) {		// 注意 for 里面用的都是 ranks[i] 
		// 计算 a < b 且 s[a] < s[b] 的情况 
		score[ranks[i]] = query(ranks[i] - 1, c);
		add(ranks[i], 1, c);
		// 计算 b < c 且 s[b] < s[c] 的情况 
		ans += query(ranks[i] - 1, d);
		add(ranks[i], score[ranks[i]], d);
	}
	return ans;
}
int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i].val), a[i].id = i;
		sort(a + 1, a + n + 1, cmp);
		ll ans = 0;
		for (int i = 1; i <= n; i++)
			b[i] = a[i];
		ans += sovle();
		for (int i = 1; i <= n; i++)
			b[i] = a[n - i + 1];
		ans += sovle();		// 反过来再算 
		printf("%lld\n", ans);
	}
	return 0;
}

语文不好, 表述的可能不是很清楚.

posted @ 2021-06-28 23:39  dbg_8  阅读(56)  评论(0)    收藏  举报