Codeforces 765F Souvenirs - 莫队算法 - 链表 - 线段树
Solution 1 Mo's Algorithm & Linked List
不会。。果断莫队
当插入一个数的时候,如果用set维护前驱后继,然后结果是:$O((n + m)\sqrt{n}\log n)$,没救的,卡不过去。
但是感觉原序列不会改变,使用set维护很浪费。
考虑链表,链表上的每个数的后继是这个数的后继。
考虑可以通过排序提前得到大小关系,不断从链表中删数就能得到它在剩下的区间内的前后继。如果询问在同一个块内直接暴力。否则把答案分成三部分。
- 两个数都在当前块外,先从小到大加入剩下的所有数,从右到左删掉它们,再从左到右不断加入它们,然后就能得到所有前缀的答案。
- 两个数都在当前块内,处理出所有后缀的答案,做法类似。
- 一个在当前块内,一个在块外,移动右指针的时候让块内的所有数在链表内,查询的时候删掉再加入。
Code
1 /** 2 * Codeforces 3 * Problem#765F 4 * Accepted 5 * Time: 1309ms 6 * Memory: 9400k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 #define pii pair<int, int> 13 #define fi first 14 #define sc second 15 16 const int cs = 350; 17 const signed int inf = (signed) (~0u >> 1); 18 19 typedef class Query { 20 public: 21 int l, r, lid, rid, id, res; 22 23 boolean operator < (Query b) const { 24 if (lid != b.lid) return lid < b.lid; 25 return r < b.r; 26 } 27 }Query; 28 29 int n, m; 30 int *ar; 31 pii *cr; 32 int *pre, *suf, *L; 33 Query *qs; 34 35 inline void init() { 36 scanf("%d", &n); 37 ar = new int[(n + 1)]; 38 cr = new pii[(n + 1)]; 39 pre = new int[(n + 2)]; 40 suf = new int[(n + 2)]; 41 L = new int[(n + 1)]; 42 for (int i = 1; i <= n; i++) 43 scanf("%d", ar + i), cr[i].fi = ar[i], cr[i].sc = i; 44 scanf("%d", &m); 45 qs = new Query[(m + 1)]; 46 for (int i = 1; i <= m; i++) { 47 scanf("%d%d", &qs[i].l, &qs[i].r); 48 qs[i].lid = (qs[i].l - 1) / cs, qs[i].rid = (qs[i].r - 1) / cs; 49 qs[i].id = i; 50 } 51 } 52 53 void add(int p) { 54 suf[pre[p]] = p; 55 pre[suf[p]] = p; 56 } 57 58 void remove(int p) { 59 suf[pre[p]] = suf[p]; 60 pre[suf[p]] = pre[p]; 61 } 62 63 int update(int p) { 64 int rt = inf; 65 if (pre[p]) 66 rt = ar[p] - ar[pre[p]]; 67 if (suf[p] <= n) 68 rt = min(rt, ar[suf[p]] - ar[p]); 69 return rt; 70 } 71 72 inline void solve() { 73 sort(cr + 1, cr + n + 1); 74 sort(qs + 1, qs + m + 1); 75 int c = 1; 76 for (int sid = 0; sid <= n / cs && c <= m; sid++) { 77 int mdzzr = min(cs * (sid + 1), n), ls = 0, lr = cs * sid, ce = mdzzr; 78 79 pre[0] = 0, ls = 0; 80 for (int i = 1; i <= n; i++) { 81 pre[cr[i].sc] = ls; 82 suf[ls] = cr[i].sc; 83 ls = cr[i].sc; 84 } 85 suf[n + 1] = n + 1, pre[n + 1] = ls, suf[ls] = n + 1; 86 87 for (int i = 1; i <= mdzzr; i++) 88 remove(i); 89 for (int i = n; i > mdzzr; i--) 90 remove(i); 91 L[mdzzr] = inf; 92 for (int i = mdzzr + 1; i <= n; i++) 93 add(i), L[i] = min(L[i - 1], update(i)); 94 for (int i = mdzzr; i > lr; i--) 95 add(i); 96 for (int i = n; i > mdzzr; i--) 97 remove(i); 98 99 for ( ; c <= m && qs[c].lid == sid; c++) { 100 int l = qs[c].l, r = qs[c].r; 101 if (qs[c].lid == qs[c].rid) { 102 for (int i = lr + 1; i < l; i++) 103 remove(i); 104 for (int i = mdzzr; i >= l; i--) 105 remove(i); 106 int res = inf; 107 for (int i = l; i <= r; i++) 108 add(i), res = min(res, update(i)); 109 for (int i = r + 1; i <= mdzzr; i++) 110 add(i); 111 for (int i = l - 1; i > lr; i--) 112 add(i); 113 qs[c].res = res; 114 } else { 115 while (mdzzr < r) 116 add(++mdzzr); 117 int res = inf; 118 for (int i = lr + 1; i <= ce; i++) 119 remove(i); 120 for (int i = ce; i >= l; i--) 121 add(i), res = min(res, update(i)); 122 for (int i = l - 1; i > lr; i--) 123 add(i); 124 qs[c].res = min(res, L[r]); 125 } 126 } 127 } 128 129 for (int i = 1; i <= m; i++) 130 while (qs[i].id != i) 131 swap(qs[i], qs[qs[i].id]); 132 for (int i = 1; i <= m; i++) 133 printf("%d\n", qs[i].res); 134 } 135 136 int main() { 137 init(); 138 solve(); 139 return 0; 140 }
Solution 2 Segment Tree
考虑将询问离线,然后分别考虑大于等于位置$i$上的数和小于等于它的数产生的贡献。从左到右扫描数组。
假设当前考虑以$r$为右端点的询问区间的答案。那么就要将$r$能产生的贡献计算出来。
能产生贡献的位置是在$r$前大于等于$a_r$的一个递减数列(从后向前)。根据它的期望长度是$log_{n}$的,用个线段树区间取min,就可以水掉bzoj上面的某道题。
于是cf上成功T掉了。
加一个很强的剪枝:新找到的数和$a_r$的差必须小于上一个找到的差的一半。
为什么是正确的呢?因为包含新找到的这个位置和$r$的区间一定包含上一个找到的数,但是显然新找到的数和上一个找到的数的差更优,会在另一次扫描中被统计。
至于查找上一个在某个值域内最后出现的数的位置,再开一棵线段树。
Code
1 /** 2 * Codeforces 3 * Problem#765F 4 * Accepted 5 * Time: 997ms 6 * Memory: 28600k 7 */ 8 #include <algorithm> 9 #include <iostream> 10 #include <cstdlib> 11 #include <cstdio> 12 using namespace std; 13 typedef bool boolean; 14 15 const signed int inf = (signed) (~0u >> 1), Val = 1e9; 16 17 typedef class SegTreeNode { 18 public: 19 int val; 20 SegTreeNode *l, *r; 21 22 SegTreeNode() { } 23 24 void pushUp() { 25 if (l) 26 val = l->val; 27 if (r) 28 val = max(val, r->val); 29 } 30 }SegTreeNode; 31 32 SegTreeNode pool[2000000]; 33 SegTreeNode *top = pool; 34 35 SegTreeNode* newnode() { 36 top->l = top->r = NULL; 37 return top++; 38 } 39 40 typedef class SegTree { 41 public: 42 SegTreeNode* rt; 43 44 SegTree():rt(NULL) { } 45 46 void update(SegTreeNode*& p, int l, int r, int ql, int qr, int val) { 47 if (!p) 48 p = newnode(), p->val = inf; 49 if (l == ql && r == qr) { 50 p->val = min(p->val, val); 51 return ; 52 } 53 int mid = (l + r) >> 1; 54 if (qr <= mid) 55 update(p->l, l, mid, ql, qr, val); 56 else if (ql > mid) 57 update(p->r, mid + 1, r, ql, qr, val); 58 else { 59 update(p->l, l, mid, ql, mid, val); 60 update(p->r, mid + 1, r, mid + 1, qr, val); 61 } 62 } 63 64 int query(SegTreeNode *p, int l, int r, int idx) { 65 if (!p) 66 return inf; 67 if (l == r) 68 return p->val; 69 int mid = (l + r) >> 1, a = p->val, b = 0; 70 if (idx <= mid) 71 b = query(p->l, l, mid, idx); 72 else 73 b = query(p->r, mid + 1, r, idx); 74 return min(a, b); 75 } 76 77 void update(SegTreeNode* &p, int l, int r, int idx, int val) { 78 if (!p) 79 p = newnode(), p->val = -1; 80 if (l == r) { 81 p->val = val; 82 return; 83 } 84 int mid = (l + r) >> 1; 85 if (idx <= mid) 86 update(p->l, l, mid, idx, val); 87 else 88 update(p->r, mid + 1, r, idx, val); 89 p->pushUp(); 90 } 91 92 int query(SegTreeNode* p, int l, int r, int ql, int qr) { 93 if (!p) 94 return -1; 95 if (l == ql && r == qr) 96 return p->val; 97 int mid = (l + r) >> 1; 98 if (qr <= mid) 99 return query(p->l, l, mid, ql, qr); 100 if (ql > mid) 101 return query(p->r, mid + 1, r, ql, qr); 102 int a = query(p->l, l, mid, ql, mid); 103 int b = query(p->r, mid + 1, r, mid + 1, qr); 104 return max(a, b); 105 } 106 }SegTree; 107 108 typedef class Query { 109 public: 110 int l, r, id, res; 111 112 boolean operator < (Query b) const { 113 return r < b.r; 114 } 115 }Query; 116 117 int n, m; 118 int *ar; 119 SegTree st, stv; 120 Query* qs; 121 122 inline void init() { 123 scanf("%d", &n); 124 ar = new int[(n + 1)]; 125 for (int i = 1; i <= n; i++) 126 scanf("%d", ar + i); 127 scanf("%d", &m); 128 qs = new Query[(m + 1)]; 129 for (int i = 1; i <= m; i++) { 130 scanf("%d%d", &qs[i].l, &qs[i].r); 131 qs[i].id = i, qs[i].res = inf; 132 } 133 } 134 135 inline void solve() { 136 sort(qs + 1, qs + m + 1); 137 for (int s = 0, c = 0; s < 2; s++) { 138 st.rt = stv.rt = NULL, top = pool, c = 1; 139 for (int i = 1; i <= n; i++) { 140 int rlim = Val, idx; 141 while (ar[i] <= rlim && (idx = stv.query(stv.rt, 0, Val, ar[i], rlim)) != -1) { 142 st.update(st.rt, 1, n, 1, idx, ar[idx] - ar[i]); 143 rlim = ((ar[idx] + ar[i] - 1) >> 1); 144 } 145 stv.update(stv.rt, 0, Val, ar[i], i); 146 for ( ; c <= m && qs[c].r == i; c++) 147 qs[c].res = min(qs[c].res, st.query(st.rt, 1, n, qs[c].l)); 148 } 149 for (int i = 1; i <= n; i++) 150 ar[i] = Val - ar[i]; 151 } 152 153 for (int i = 1; i <= m; i++) 154 while (qs[i].id != i) 155 swap(qs[i], qs[qs[i].id]); 156 for (int i = 1; i <= m; i++) 157 printf("%d ", qs[i].res); 158 } 159 160 int main() { 161 init(); 162 solve(); 163 return 0; 164 }