https://vjudge.net/problem/UVALive-4329
题意:
一条大街上住着n个兵乓球爱好者,经常组织比赛切磋技术。每个人都有一个不同的技能值ai。每场比赛需要3个人:两名选手,一名裁判。他们有一个奇怪的规定,即裁判必须住在两名选手的中间,并且技能值也在两名选手之间。问一共能组织多少种比赛。
思路:
想了好久才明白二叉索引树在这里的运用。
首先,就像训练指南上说的,考虑第i个人当裁判的情形。假设a1到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。
接下来就是二叉索引树的应用,我们从左往右扫描a[i],计算c[]值。
1 #include<iostream> 2 #include<algorithm> 3 #include<string> 4 #include<cstring> 5 using namespace std; 6 7 const int maxn = 100000 + 5; 8 int n; 9 int a[maxn],c[maxn],s1[maxn],s2[maxn]; 10 11 12 int lowbit(int x) 13 { 14 return x&-x; 15 } 16 17 int sum(int x) 18 { 19 int ret = 0; 20 while (x > 0) 21 { 22 ret += c[x]; 23 x -= lowbit(x); 24 } 25 return ret; 26 } 27 28 void add(int x, int d) 29 { 30 while (x <= maxn) 31 { 32 c[x] += d; 33 x += lowbit(x); 34 } 35 } 36 37 int main() 38 { 39 ios::sync_with_stdio(false); 40 //freopen("D:\txt.txt", "r", stdin); 41 int T; 42 cin >> T; 43 while (T--) 44 { 45 cin >> n; 46 for (int i = 1; i <= n; i++) 47 cin >> a[i]; 48 memset(c, 0, sizeof(c)); 49 for (int i = 1; i <= n; i++) 50 { 51 add(a[i], 1); 52 s1[i] = sum(a[i] - 1); 53 } 54 memset(c, 0, sizeof(c)); 55 for (int i = n; i >= 1; i--) 56 { 57 add(a[i], 1); 58 s2[i] = sum(a[i] - 1); 59 } 60 long long ans = 0; 61 for (int i = 2; i <= n-1; i++) 62 { 63 ans += s1[i] * (n - i - s2[i]) + (i - s1[i] - 1)*s2[i]; 64 } 65 cout << ans << endl; 66 } 67 }