Codeforces 765F Souvenirs - 莫队算法 - 链表

Posted yyf0309

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 765F Souvenirs - 莫队算法 - 链表相关的知识,希望对你有一定的参考价值。

题目传送门

  神速的列车

  光速的列车

  声速的列车

题目大意

  给定一个长度为$n$的序列,$m$次询问区间$[l, r]$内相差最小的两个数的差的绝对值。

  不会。。果断莫队(orz mcfx,一讲“值域”链表就明白可以干什么了)。

  当插入一个数的时候,如果用set维护前驱后继,然后结果是:$O((n + m)sqrt{n}log n)$,没救的,卡不过去。

  但是感觉原序列不会改变,使用set维护很浪费。

  考虑链表。注意到链表能够$O(1)$完成插入和删除。

  然而这对我们解决问题没什么用,考虑对于按照一定顺序删除,然后逆序恢复这些被删除的点也是可行的。例如我依次删掉1,2,3。然后依次加入3,2,1.这中间我都能正确地找回前驱后继。

  这是一条优美的性质。

  由于答案不支持删除,考虑回滚莫队。

  当左端点在一块内的时候,首先从小到大(不是下标,是数值)建出链表。按照回滚莫队的方式,右端点需要从下一块块首开始向右扩展,因此从1开始,向后删掉链表中的元素,直到当前块块尾。然后从数组尾向前删除元素,直到链表被清空。然后从块尾的后一个位置开始向后加入元素,并记录当前区间的答案(因为你可以在链表中查询一个元素的前驱后继)。

  然后从块尾遍历到块首,依次加入元素,然后从后面删掉块外的元素。

  预处理部分就结束了,接着考虑处理询问。

  • 当询问区间在同一块内时。从块首删到询问的左端点(不包含它),然后从块尾删到询问的左端点。然后反着加入元素,边加边更新答案。最后加到块尾,最后将块左端删掉的一部分加回去。
  • 当询问区间跨过块端点时,从移动莫队右指针,加入元素。然后删掉当前块内的所有元素,从块尾加入元素,加到左端点,不断更新左半边产生的答案。那右半边本身的答案呢?之前记录了。然后两者取个最小值就是答案。

  确实好像有点繁琐,写起来还是不难受。详情可以看代码,语文不太好讲不清楚。

  因为要保证删掉的元素还能正确的找回来,所以需要进行辣么多次感觉是没用的删除。就因为这个常数比普通莫队大个2~3倍以上。然后codeforces上过了,floj上就T掉了。sad。

  不过感觉给个空限10M就能卡掉若干主席树做法。(果然最毒瘤的不是卡时间,而是卡空间。)

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
", qs[i].res);
134 }
135 
136 int main() {
137     init();
138     solve();    
139     return 0;
140 }

以上是关于Codeforces 765F Souvenirs - 莫队算法 - 链表的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces 765F Souvenirs - 莫队算法 - 链表

Souvenirs CodeForces - 765F (线段树好题)

[CF765F]Souvenirs

CF765F Souvenirs

Selling Souvenirs CodeForces - 808E (分类排序后DP+贪心)

Codeforces 808 E. Selling Souvenirs(三分)