Queue

题目描述

一个简单的数列问题:

给定一个长度为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\n", ans);
66     return 0;
67 }

 

posted @ 2018-03-03 18:21  mrclr  阅读(319)  评论(0编辑  收藏  举报