二分+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)

 

你可能感兴趣的:(sign)