码迷,mamicode.com
首页 > 其他好文 > 详细

更多的莫队

时间:2017-12-03 12:59:15      阅读:185      评论:0      收藏:0      [点我收藏+]

标签:细节   gif   enter   com   void   题目   数据   color   logs   

带修改莫队

  有这么一类问题,毒瘤数据结构题目(比如什么树套树套树),除了询问还有修改,然而支持离线,能快速地在区间首尾添加/删除元素。

  普通莫队可以看做是将每个询问看成了一个二元组 (l, r) 。每次修改可以看成时间往后移动了一个单位。所以对于带修改莫队将每个询问看成三元组 (l, r, t) 、

  如何确定较为高效的处理查询的顺序?

  继续参考普通莫队,将前两维分块,按块编号排序,然后按时间为第三关键字升序排序。

  在进行处理询问的过程中,还要记录一个当前时间,当处理到询问i,它的时间和当前时间不一样,就要通过撤销/执行某些操作来使时间一样。

  那。。如何确定块大小呢?

  现在假设块大小为技术分享图片,那么一共就有技术分享图片块。假设询问操作和序列长度同级,意思是它们的范围都是n。现在用当每次修改时间复杂度和每次移动指针的时间复杂度均为为O(1)来举例子。

  • 对于l指针的移动:对于每个询问,左指针至多移动技术分享图片次,所以对于左指针移动的总时间复杂度为O(技术分享图片)。
  • 对于r指针的移动:同理上,可也以得到它移动的总时间复杂度为O(技术分享图片)。
  • 对于t指针的移动:考虑当l处于块i,r处于块j时,处理完满足左端点在块i内,右端点在块j内的时间复杂度,因为t是递增的,所以时间复杂度为O(n),又因为总共有技术分享图片个块对,所以时间指针的总时间复杂度为技术分享图片

   为了使三个时间复杂度的最大值最小,所以令

技术分享图片

  然后解得

技术分享图片

  所以当技术分享图片,带修改莫队时间复杂度有最小值技术分享图片。(决定TLE还是AC的三分之一)

  然后是道模板题的代码[题目传送门]:

Code

技术分享图片
  1 /**
  2  * luogu
  3  * Problem#1903
  4  * Accepted
  5  * Time: 100ms
  6  * Memory: 2531k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 typedef pair<int, int> pii;
 12 #define fi first
 13 #define sc second
 14 
 15 typedef class Query {
 16     public:
 17         int l;
 18         int r;
 19         int id;
 20         int t;
 21         
 22         Query(int l = 0, int r = 0, int id = 0, int t = 0):l(l), r(r), id(id), t(t) {    }
 23 }Query;
 24 
 25 #define BucketSize 1000005
 26 
 27 int n, m;
 28 int cs = 460;
 29 int co = 0, cq = 0;
 30 Query *chs;
 31 pii* opts;
 32 int* ar;
 33 int bucket[BucketSize];
 34 
 35 boolean operator < (const Query& a, const Query& b) {
 36     if(a.l / cs != b.l / cs)    return a.l / cs < b.l / cs;
 37     if(a.r / cs != b.r / cs)    return a.r / cs < b.r / cs;
 38     return a.t < b.t;
 39 }
 40 
 41 inline void init() {
 42     scanf("%d%d", &n, &m);
 43     chs = new Query[(m + 1)];
 44     opts = new pii[(m + 1)];
 45     ar = new int[(n + 1)];
 46     
 47     for(int i = 1; i <= n; i++)
 48         scanf("%d", ar + i);
 49     
 50     char buf[5];
 51     for(int i = 1, x, y; i <= m; i++) {
 52         scanf("%s%d%d", buf, &x, &y);
 53         if(buf[0] == Q) {
 54             cq++;
 55             chs[cq] = Query(x, y, cq, co);
 56         } else {
 57             opts[++co] = pii(x, y);
 58         }
 59     }
 60 }
 61 
 62 int cur;
 63 int mdzzl, mdzzr, mdzzt;
 64 int* ans;
 65 
 66 inline void update(int p, int sign) {
 67     bucket[p] += sign;
 68     if(bucket[p] == 1 && sign == 1)    cur++;
 69     if(bucket[p] == 0 && sign == -1)    cur--;
 70 }
 71 
 72 pii *bt;
 73 inline void update(pii& op, boolean back) {
 74     if(op.fi >= mdzzl && op.fi <= mdzzr) {
 75         update(ar[op.fi], -1);
 76         update(op.sc, 1);
 77     }
 78     if(!back)
 79         bt[mdzzt] = pii(op.fi, ar[op.fi]);
 80     ar[op.fi] = op.sc;
 81 }
 82 
 83 inline void solve() {
 84     ans = new int[(cq + 1)];
 85     bt = new pii[(n + 1)];
 86     sort(chs + 1, chs + cq + 1);
 87     mdzzl = 1, mdzzr = 0;
 88     for(int i = 1; i <= cq; i++) {
 89         while(mdzzr < chs[i].r)    update(ar[++mdzzr], 1);
 90         while(mdzzr > chs[i].r)    update(ar[mdzzr--], -1);
 91         while(mdzzl < chs[i].l)    update(ar[mdzzl++], -1);
 92         while(mdzzl > chs[i].l) update(ar[--mdzzl], 1);
 93         while(mdzzt < chs[i].t)    update(opts[++mdzzt], false);
 94         while(mdzzt > chs[i].t)    update(bt[mdzzt--], true);
 95         ans[chs[i].id] = cur;
 96     }
 97     for(int i = 1; i <= cq; i++)
 98         printf("%d\n", ans[i]);
 99 }
100 
101 int main() {
102     init();
103     solve();
104     return 0;
105 }
带修改莫队模板

 

回滚莫队

   再来看一类问题,仍然是毒瘤数据结构题,虽然木有修改但是可以快速在区间首或者区间尾插入元素但是难以删除,答案也不复杂(有时可能就1两个数)。

   既然难以删除就思考如何避免删除的过程。

  有注意到普通莫队的左端点在一个块内到处乱跳,考虑如果从块尾不断添加元素到询问的左端点?

  显然这样是可行的,处理完这个询问,直接将左端点还原到块尾。只不过这里不是通过一个一个地删除元素来还原,而是在移动左端点前,备份答案,处理完询问后还原一下维护的量然后直接还原答案。但是有一个问题:如果询问的左右端点在同一块?

  直接暴力好了,反正时间复杂度不超过技术分享图片(此处的技术分享图片是每次插入的时间复杂度)

  然后要注意一个问题就是莫队的主过程要考虑询问的边界问题。

  然后是道模板题的代码[题目传送门]:

Code

技术分享图片
  1 /**
  2  * bzoj
  3  * Problem#4241
  4  * Accepted
  5  * Time: 15408ms
  6  * Memory: 5208k
  7  */
  8 #include <bits/stdc++.h>
  9 #ifdef WIN32
 10 #define Auto "%I64d"
 11 #else
 12 #define Auto "%lld"
 13 #endif
 14 using namespace std;
 15 typedef bool boolean;
 16 #define ll long long
 17 
 18 const int cs = 350;
 19 
 20 typedef class Query {
 21     public:
 22         int l, r, id;
 23         
 24         Query(int l = 0, int r = 0, int id = 0):l(l), r(r), id(id) {    }
 25         
 26         boolean operator < (Query b) const {
 27             if(l / cs != b.l / cs)    return l / cs < b.l / cs;
 28             return r < b.r; 
 29         }
 30 }Query;
 31 
 32 int n, m;
 33 int *ar;
 34 Query* qs;
 35 int *nar, *val;
 36 
 37 inline void init() {
 38     scanf("%d%d", &n, &m);
 39     ar = new int[(n + 1)];
 40     nar = new int[(n + 1)];
 41     val = new int[(n + 1)];
 42     qs = new Query[(m + 1)];
 43     
 44     for(int i = 1; i <= n; i++)
 45         scanf("%d", ar + i);
 46     for(int i = 1; i <= m; i++)
 47         scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].id = i;
 48 }
 49 
 50 inline void decrease() {
 51     memcpy(val, ar, sizeof(int) * (n + 1));
 52     sort(val + 1, val + n + 1);
 53     for(int i = 1; i <= n; i++)
 54         nar[i] = lower_bound(val + 1, val + n + 1, ar[i]) - val;
 55 }
 56 
 57 ll* ans;
 58 ll cans = 0, hans = 0;
 59 ll *bucket;
 60 
 61 inline void update(int p) {
 62     if((bucket[nar[p]] += ar[p]) > cans)
 63         cans = bucket[nar[p]];
 64 }
 65 
 66 inline void solve() {
 67     bucket = new ll[(n + 1)];
 68     ans = new ll[(m + 1)];
 69     memset(bucket, 0, sizeof(ll) * (n + 1));
 70     sort(qs + 1, qs + m + 1);
 71     int mdzzl, mdzzr, bl, f = 1;
 72     for(int id = 0; f <= m; id++) {
 73         while(f <= m && qs[f].l / cs == id && qs[f].r / cs == id) {
 74             mdzzl = qs[f].l, mdzzr = qs[f].l - 1, cans = 0;
 75             while(mdzzr < qs[f].r)    update(++mdzzr);
 76             ans[qs[f].id] = cans;
 77             for(int i = mdzzl; i <= mdzzr; i++)
 78                 bucket[nar[i]] = 0;
 79             f++;
 80         }
 81         mdzzl = bl = (id + 1) * cs, mdzzr = mdzzl - 1, cans = 0;
 82         while(f <= m && qs[f].l / cs == id) {
 83             while(mdzzr < qs[f].r)    update(++mdzzr);
 84             hans = cans;
 85             while(mdzzl > qs[f].l)    update(--mdzzl);
 86             ans[qs[f].id] = cans;
 87             while(mdzzl < bl && mdzzl < n)    bucket[nar[mdzzl]] -= ar[mdzzl], mdzzl++;
 88             cans = hans;
 89             f++;
 90         }
 91         for(int i = bl; i <= mdzzr; i++)
 92             bucket[nar[i]] = 0;
 93     }
 94     for(int i = 1; i <= m; i++)
 95         printf(Auto"\n", ans[i]);
 96 }
 97             
 98 int main() {
 99     init();
100     decrease();
101     solve();
102     return 0;
103 }
回滚莫队模板(bzoj 历史研究)

树上莫队

  求出树的括号序列,然后每个树上路径的问题就转化成括号序列上的区间问题。

  (可能有细节没有想清楚)


在线莫队

  (不会QAQ,打上Lazy Tag)

 

更多的莫队

标签:细节   gif   enter   com   void   题目   数据   color   logs   

原文地址:http://www.cnblogs.com/yyf0309/p/More_Mos_Algorithm.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!