题意:一条大街上任着n个乒丘球爱好者,经常组织比赛切磋技术。每个人都有一个不同技能值a[i],每场比赛需要3个人: 两名选手,一名裁判。他们有一个奇怪的规定,即裁判必须住在两名选手的中间,并且技能值也在两名选手之间。问一共能组织多少种比赛。
【输入格式】
输入第一行为数据组数T(1<T<20)。每组数据占一行,首先是整数n(3<=n<=20000)然后是个n不同的数即a[i]...按照住所从左到右的顺序给出每个乒乓爱好者的技能值。
【输出格式】
对于每组数据,输出比赛总数的值。
【分析】
【输入格式】
输入第一行为数据组数T(1<T<20)。每组数据占一行,首先是整数n(3<=n<=20000)然后是个n不同的数即a[i]...按照住所从左到右的顺序给出每个乒乓爱好者的技能值。
【输出格式】
对于每组数据,输出比赛总数的值。
【分析】
考虑第i个人当裁判的情形。假设a[i]到a[n] 中有L[i]个比a小,那么就有(i-1)-L[i]个比a[i]大,同理假设a[i+1]到a[n]中有R[i]个比a[i]小,那么就有(n-i)-R[i]个比a[i]。根据乘法原理和加法原理,i当裁判有L[i]*(n-i-R[i]) + (i-L[i]-1)*R[i]种比赛。这样问题就转化为求L[i]和d[i]了。
L[i]可以这样计算:从左到右扫描所有的a[i],x[j]表示到目前为止已经考虑过得a[i]中,是否存在一个a[i]=j(x[j]=0表示j不存在,1表示存在),则C[i]就是前缀和x[1]+x[2]+...x[a-1]。初始时所有x[i]=0,在计算C[i]时,需要先设x[a[i]]=1,然后求前缀和。换句话说,我们需要动态的修改单个元素值并求其中你会和———这正是BIT的标准用法。这样,就可以在O(nlogr)(这里的r是a[i]的上限)的时间内计算出所有L[i]和R[i],然后在O(n)时间里累加出答案。
以上内容来自算法竞赛入门经典
注意本题中树状数组的大小是a[i]的上限不是个数N。
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; int n, a[MAXN], C[MAXN], l[MAXN], r[MAXN]; int lowbit(int x){ return x&(-x); } int sum(int x) { int ans = 0; while (x > 0) { ans += C[x]; x -= lowbit(x); } return ans; } void add(int x, int d) { while (x < MAXN) { C[x] += d; x += lowbit(x); } } int main() { int T; scanf("%d", &T); while (T--) { memset(C, 0, sizeof(C)); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); l[i] = sum(a[i]-1); add(a[i], 1); } memset(C, 0, sizeof(C)); for (int i = n; i >= 1; i--) { r[i] = sum(a[i]-1); add(a[i], 1); } long long ans = 0; for (int i = 2; i < n; i++) { ans += l[i]*(n-i-r[i]) + (i-l[i]-1)*r[i]; } printf("%lld\n", ans); } return 0; } /* 1 3 1 2 3 */