二分+RMQ/双端队列/尺取法 HDOJ 5289 Assignment

 

题目传送门

 1 /*
 2     题意:问有几个区间最大值-最小值 < k
 3     解法1:枚举左端点,二分右端点,用RMQ(或树状数组)求区间最值,O(nlog(n))复杂度
 4     解法2:用单调队列维护最值,O(n)复杂度,用法
 5     解法3:尺取法,用mutiset维护最值
 6 */
 7 #include <cstdio>
 8 #include <algorithm>
 9 #include <cstring>
10 #include <cmath>
11 using namespace std;
12 
13 typedef long long ll;
14 const int MAXN = 1e5 + 10;
15 const int INF = 0x3f3f3f3f;
16 int a[MAXN];
17 int mn[MAXN][20], mx[MAXN][20];     //最多能保存524288的长度
18 
19 int RMQ(int l, int r)   {
20     int k = 0;  while (1<<(k+1) <= r - l + 1) k++;      //令k为满足1<<k <= r-l+1的最大整数
21     int MAX = max (mx[l][k], mx[r-(1<<k)+1][k]);        //意思是区间最左边1<<k长度的最大值和最右边1<<k长度的最大值
22     int MIN = min (mn[l][k], mn[r-(1<<k)+1][k]);        //可能有重叠的地方
23     return MAX - MIN;
24 }
25 
26 int main(void)  {       //HDOJ 5289 Assignment
27     freopen ("B.in", "r", stdin);
28 
29     int t;  scanf ("%d", &t);
30     while (t--) {
31         int n, k;  scanf ("%d%d", &n, &k);
32         for (int i=1; i<=n; ++i)    {
33             scanf ("%d", &a[i]);
34             mn[i][0] = mx[i][0] = a[i];
35         }
36         for (int j=1; (1<<j)<=n; ++j)   {
37             for (int i=1; i+(1<<j)-1<=n; ++i)   {
38                 mn[i][j] = min (mn[i][j-1], mn[i+(1<<(j-1))][j-1]);     //mn[i][j]意思是从i开始,长度1<<j的区间的最小值
39                 mx[i][j] = max (mx[i][j-1], mx[i+(1<<(j-1))][j-1]);
40             }
41         }
42 
43         ll ans = 0;
44         for (int i=1; i<=n; ++i)    {
45             int l = i, r = n;
46             while (l + 1 < r)   {       //二分使得l, r最远
47                 int mid = (l + r) >> 1;
48                 if (RMQ (i, mid) < k)   l = mid;
49                 else    r = mid;
50             }
51             if (RMQ (i, r) < k) {       //此时[l, r](l+1==r) 其中一个一定满足条件
52                 ans += (r - i + 1);
53             }
54             else    {
55                 ans += (l - i + 1);
56             }
57         }
58         printf ("%I64d\n", ans);
59     }
60 
61     return 0;
62 }
 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <cmath>
 5 using namespace std;
 6 
 7 typedef long long ll;
 8 const int MAXN = 1e5 + 10;
 9 const int INF = 0x3f3f3f3f;
10 int a[MAXN], n;
11 struct BIT  {
12     int mn[MAXN], mx[MAXN];
13 
14     void init(void) {
15         memset (mn, INF, sizeof (mn));
16         memset (mx, 0, sizeof (mx));
17     }
18     void add_min(int i, int x)  {
19         while (i <= n)    {
20             mn[i] = min (mn[i], x); i += i & (-i);
21         }
22     }
23     int query_min(int i)    {
24         int res = INF;
25         while (i > 0)   {
26             res = min (res, mn[i]); i -= i & (-i);
27         }
28         return res;
29     }
30     void add_max(int i, int x)  {
31         while (i <= n)    {
32             mx[i] = max (mx[i], x); i += i & (-i);
33         }
34     }
35     int query_max(int i)    {
36         int res = 0;
37         while (i > 0)   {
38             res = max (res, mx[i]); i -= i & (-i);
39         }
40         return res;
41     }
42 }bit;
43 
44 int main(void)  {
45     //freopen ("B.in", "r", stdin);
46 
47     int t;  scanf ("%d", &t);
48     while (t--) {
49         int k;  scanf ("%d%d", &n, &k);
50         for (int i=1; i<=n; ++i)    {
51             scanf ("%d", &a[i]);
52         }
53         
54         ll ans = 0; bit.init ();
55         for (int i=n; i>=1; --i)    {       //树状数组的特点,倒过来插入,求[i, n]区间
56             bit.add_min (i, a[i]);
57             bit.add_max (i, a[i]);
58             int l = i, r = n;
59             while (l <= r)  {
60                 int mid = (l + r) >> 1;
61                 int MAX = bit.query_max (mid);
62                 int MIN = bit.query_min (mid);
63                 if (MAX - MIN >= k) r = mid - 1;
64                 else    l = mid + 1;
65             }
66             ans += l - i;
67         }
68         printf ("%I64d\n", ans);
69     }
70 
71     return 0;
72 }
树状数组
 1 /*
 2     维护递增和递减的队列,当队首满足条件时,添加个数,再在从后添加元素,否则pop_front
 3 */
 4 #include <cstdio>
 5 #include <algorithm>
 6 #include <cstring>
 7 #include <queue>
 8 using namespace std;
 9 
10 typedef long long ll;
11 const int MAXN = 1e5 + 10;
12 const int INF = 0x3f3f3f3f;
13 struct Node {
14     int v, p;
15 };
16 int a[MAXN];
17 
18 int main(void)  {
19     //freopen ("B.in", "r", stdin);
20 
21     int t;  scanf ("%d", &t);
22     while (t--) {
23         int n, k;   scanf ("%d%d", &n, &k);
24         for (int i=1; i<=n; ++i)    scanf ("%d", &a[i]);
25         
26         deque<Node> Q1, Q2; ll ans = 0; int head = 1;
27         for (int i=1; i<=n; ++i)    {
28             Node now = (Node){a[i], i};
29             while (!Q1.empty ())    {       //递减  队首max
30                 Node tmp = Q1.back ();
31                 if (now.v > tmp.v)  Q1.pop_back ();
32                 else    break;
33             }
34             Q1.push_back (now);
35             while (!Q2.empty ())    {       //递增  队首min
36                 Node tmp = Q2.back ();
37                 if (now.v < tmp.v)  Q2.pop_back ();
38                 else    break;
39             }
40             Q2.push_back (now);
41 
42             if (i == 1) ans++;
43             else    {
44                 while (true)    {
45                     Node big = Q1.front ();
46                     Node small = Q2.front ();
47                     if (big.v - small.v < k)    break;
48                     else    {
49                         if (small.p < big.p)    {
50                             head = small.p + 1; Q2.pop_front ();
51                         }
52                         else    {
53                             head = big.p + 1;   Q1.pop_front ();
54                         }
55                     }
56                 }
57                 ans += i - head + 1;
58             }
59         }
60         printf ("%I64d\n", ans);
61     }
62 
63     return 0;
64 }
单调队列
 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <set>
 5 #include <cmath>
 6 using namespace std;
 7 
 8 typedef long long ll;
 9 const int MAXN = 1e5 + 10;
10 const int INF = 0x3f3f3f3f;
11 multiset<int> S;
12 int a[MAXN];
13 
14 int main(void)  {
15     //freopen ("B.in", "r", stdin);
16 
17     int t;  scanf ("%d", &t);
18     while (t--) {
19         int n, k;   scanf ("%d%d", &n, &k);
20         for (int i=1; i<=n; ++i)    {
21             scanf ("%d", &a[i]);
22         }
23 
24         S.clear (); S.insert (a[1]);
25         int l = 1, r = 2;   ll ans = 0;
26         int mn, mx;
27         while (true)   {
28             if (S.size ())  {
29                 mn = *S.begin ();
30                 mx = *S.rbegin ();
31                 if (abs (a[r] - mn) < k && abs (a[r] - mx) < k) {
32                     ans += S.size ();   S.insert (a[r++]);
33                     if (r > n) break;
34                 }
35                 else    {
36                     if (S.size ())  S.erase (S.find (a[l]));
37                     l++;
38                 }
39             }
40             else    {
41                 l = r;  S.insert (a[r++]);
42                 if (r > n) break;
43             }
44         }
45         
46         printf ("%I64d\n", ans + n);
47     }
48 
49     return 0;
50 }
尺取法(multiset)

 

转载于:https://www.cnblogs.com/Running-Time/p/4667023.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值