题目描述
一个简单的数列问题:
给定一个长度为n的数列,求这样的三个元素 ai,aj,ak 的个数,满足 ai< aj >ak,且 i<j<k。
输入输出格式
输入格式:
第1行是一个整数n(1<= n <= 50000)。
接下来n行,每行一个元素ai(0< = ai <= 32767)。
输出格式:
一个数,满足 ai< aj >ak (i < j < k) 的个数。
输入输出样例
输入样例
5 1 2 3 4 1
输出样例
6
这道题我是这么想的,就是枚举 j, 然后分别找出符合条件的 ai 和 ak,
比如一个数列:2,4,6,1,7,8,2,5,3,9,当 aj 枚举到 aj = 7 的时候,查找[1, j],发现比 aj 小的数有4个,在查找 [j, n],发现比 aj 小的数有3个,那么此时j = 5 时,满足题意的数的个数就是4 * 3=12个,ans += 12。
那这道题就是一道动态区间查询数值大小的题了,最简单的想法就是对于每一次查找,分别遍历 [1, j] 和 [j, n],然而时间复杂度是 O(n * n),只能得部分分。
于是我又看了一眼数据范围,发现 0< = ai <= 32767,于是我马上想到了用线段树,且线段树的范围是值域([0, 32767]),而不是总共的个数500000。这样查询一个 aj,就能用(Ologn)的复杂度知道比aj小的数有多少个了,为此我就用了两个线段树,一个负责处理[1, j] 的数中比 aj小的数,另一个处理[j, n]。
遍历 j 的时候,前一个线段树每一次添加 aj,后一个线段树删除a[j - 1],(所以后面的线段树要先将所有元素添加进去)然后分别查询[1, 32767]中符合题意的个数。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int maxn = 5e4 + 5; 8 typedef long long ll; 9 ll ans = 0; 10 int n, a[maxn]; 11 struct Tree //打包,方便 12 { 13 int l[4 * maxn], r[4 * maxn], num[4 * maxn]; 14 void init() 15 { 16 for(int i = 0; i < 4 * maxn; ++i) 17 l[i] = r[i] = num[i] = 0; 18 } 19 /*一定要有这个建树函数,要不然查询时可能会访问到没有声明的结点 , 20 结果就PE了。刚开始我没加,d了一个晚上都没d出来……*/ 21 void build(int L, int R, int now) 22 { 23 l[now] = L; r[now] = R; 24 if(L == R) return; 25 int mid = (L + R) >> 1; 26 build(L, mid, now << 1); 27 build(mid + 1, R, now << 1 | 1); 28 } 29 void update(int L, int R, int idx, int d, int now) 30 { 31 l[now] = L; r[now] = R; 32 if(l[now] == r[now]) 33 { 34 num[now] += d; 35 return; 36 } 37 int mid = (l[now] + r[now]) >> 1; 38 if(idx <= mid) update(L, mid, idx, d, now << 1); 39 else update(mid + 1, R, idx, d, now << 1 | 1); 40 num[now] = num[now << 1] + num[now << 1 | 1]; 41 } 42 int query(int L, int R, int now) 43 { 44 if(L == l[now] && R == r[now]) return num[now]; 45 int mid = (l[now] + r[now]) >> 1; 46 if(R <= mid) return query(L, R, now << 1); 47 else if(L > mid) return query(L, R, now << 1 | 1); 48 else return query(L, mid, now << 1) + query(mid + 1, R, now << 1 | 1); 49 } 50 }treeHead, treeTail; 51 int main() 52 { 53 treeHead.init(); treeTail.init(); 54 scanf("%d", &n); 55 treeHead.build(1, maxn, 1); treeTail.build(1, maxn, 1); 56 for(int i = 1; i <= n; ++i) {scanf("%d", &a[i]); a[i] += 2; treeTail.update(1, maxn, a[i], 1, 1);} 57 //a[i] += 2 是为了防止a[i] = 0 的情况,因为查询区间是[1, maxn] 58 treeHead.update(1, maxn, a[1], 1, 1); 59 for(int i = 2; i < n; ++i) 60 { 61 treeHead.update(1, maxn, a[i], 1, 1); treeTail.update(1, maxn, a[i - 1], -1, 1); 62 ll temp = (ll)treeHead.query(1, a[i] - 1, 1) * treeTail.query(1, a[i] - 1, 1); 63 ans += temp; 64 } 65 printf("%lld ", ans); 66 return 0; 67 }