标签:细节 gif enter com void 题目 数据 color logs
有这么一类问题,毒瘤数据结构题目(比如什么树套树套树),除了询问还有修改,然而支持离线,能快速地在区间首尾添加/删除元素。
普通莫队可以看做是将每个询问看成了一个二元组 (l, r) 。每次修改可以看成时间往后移动了一个单位。所以对于带修改莫队将每个询问看成三元组 (l, r, t) 、
如何确定较为高效的处理查询的顺序?
继续参考普通莫队,将前两维分块,按块编号排序,然后按时间为第三关键字升序排序。
在进行处理询问的过程中,还要记录一个当前时间,当处理到询问i,它的时间和当前时间不一样,就要通过撤销/执行某些操作来使时间一样。
那。。如何确定块大小呢?
现在假设块大小为,那么一共就有块。假设询问操作和序列长度同级,意思是它们的范围都是n。现在用当每次修改时间复杂度和每次移动指针的时间复杂度均为为O(1)来举例子。
为了使三个时间复杂度的最大值最小,所以令
然后解得
所以当,带修改莫队时间复杂度有最小值。(决定TLE还是AC的三分之一)
然后是道模板题的代码[题目传送门]:
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两个数)。
既然难以删除就思考如何避免删除的过程。
有注意到普通莫队的左端点在一个块内到处乱跳,考虑如果从块尾不断添加元素到询问的左端点?
显然这样是可行的,处理完这个询问,直接将左端点还原到块尾。只不过这里不是通过一个一个地删除元素来还原,而是在移动左端点前,备份答案,处理完询问后还原一下维护的量然后直接还原答案。但是有一个问题:如果询问的左右端点在同一块?
直接暴力好了,反正时间复杂度不超过(此处的是每次插入的时间复杂度)
然后要注意一个问题就是莫队的主过程要考虑询问的边界问题。
然后是道模板题的代码[题目传送门]:
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 }
求出树的括号序列,然后每个树上路径的问题就转化成括号序列上的区间问题。
(可能有细节没有想清楚)
(不会QAQ,打上Lazy Tag)
标签:细节 gif enter com void 题目 数据 color logs
原文地址:http://www.cnblogs.com/yyf0309/p/More_Mos_Algorithm.html