UVA1428 Ping pong(树状数组)
题目链接。
题意:
n个人,从左到右排列,每个人都有一个不同的技能值 ai, 每场比赛3个人: 两名选手, 一名裁判。 裁判必须在两名选手中间, 并且技能值也在两名选手之间, 问一共能组织多少种比赛。
分析:
考虑第i个人当裁判的情形。假设 ai 到 ai-1 中有 ci 个比 ai 个小, 那么就有 (i-1)-ci 个比 ai 大; 同理, 假设 ai+1 到 an 中有di个比ai 小, 那么就有 (n-i)-di 个比 ai 大。 根据乘法原理和加法原理, i当裁判 有 ci(n-i-di)+(i-ci-1)*di 种比赛。 这样问题转化为求 ci 和 di.
题目已经明确每个人都有一个不同的技能值。 那么要求 ci , 先将树状数组 x 清空, 从左到右扫描 ai , 每运行一次 add(ai, 1), 就运行一次 sum(ai-1), sum求的是比 ai 小的数的个数, 因为是依次添加, 求 ai 时, ai 右边的数是没有加入树状数组 x 的, 这样 sum(ai-1), 求的就是 在 i 左边的比 ai 小的数的个数, 即 ci。 同理可以计算出 di。
注意:
有一个调试了4个多小时的BUG(欲哭无泪啊), 那就是 数组 x 的大小并不是 n , 因为 n 代表人的个数,而 x 存的值为技能值, 即 x 的大小为 a1~an 最大的技能值。
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 100000 + 10; int c[20000+10], d[20000+10], x[maxn], a[20000+10], n, mn; int lowbit(int e) { return e & (-e); } void add(int p, int ad){ while(p<=mn){ //这里是 mn, 并非 n x[p] += ad; p += lowbit(p); } } int sum(int p){ int ret = 0; while(p > 0){ ret += x[p]; p -= lowbit(p); } return ret; } int main(){ int T; scanf("%d", &T); while(T--){ mn = 0; scanf("%d", &n); for(int i=1; i<=n; i++){ scanf("%d", &a[i]); mn = max(mn, a[i]); } memset(x, 0, sizeof(x)); for(int i=1; i<=n; i++){ add(a[i], 1); c[i] = sum(a[i]-1); } memset(x, 0, sizeof(x)); for(int i=n; i>=1; i--){ add(a[i], 1); d[i] = sum(a[i]-1); } long long int ans = 0; for(int i=1; i<=n; i++){ ans += (long long)c[i]*(n-i-d[i]) + (long long)(i-c[i]-1)*d[i]; } printf("%lld\n", ans); } return 0; }