Codeforces 765F Souvenirs - 莫队算法 - 链表 - 线段树

题目传送门

  神速的列车

  光速的列车

  声速的列车

题目大意

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

Solution 1 Mo's Algorithm & Linked List

  不会。。果断莫队(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\n", qs[i].res);
134 }
135 
136 int main() {
137     init();
138     solve();    
139     return 0;
140 }
Mo's Algorithm

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 }

 

转载于:https://www.cnblogs.com/yyf0309/p/9278425.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值