CDQ分治学习笔记

半城伤御伤魂 提交于 2020-10-06 07:03:20

一层CDQ分治能将动态问题转化为静态问题, 对约束条件进行简化。

 

求解$n$维偏序。

对于每一个$n$元组$(a, b, c, d, ...)$。

首先按照a维排序, 问题就转化为了动态的$n-1$维偏序。

此时的$n - 1$元组$(b, c, d, ...)$包含查询/修改两个操作。

然后对序列进行CDQ分治,即保证$a$维/时间有序,

使其变成了静态的$n-1$偏序。

每次只需考虑左子区间的修改对右子区间的查询的影响。

嘿嘿嘿, 又是一个静态问题。

再按照b维排序, 又变成了动态的$n - 2$维偏序。

就这样套娃, 在问题成为2维偏序时直接解决就行了。

每套一次CDQ分治都会多个 log , 所以$k$维偏序时间复杂度应为$O(nlog^{k - 1}n)$

蒟蒻感觉CDQ套CDQ比内层数据结构如 BIT 的代码更加优美(逃

但好像要慢一些…… 下面就给出三维偏序的代码吧:)

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 const int N = 100000;
 5 
 6 struct Node {
 7     bool opt;
 8     int a, b, c, Cnt, Ans, Loc;
 9 } S1[N + 10], S2[N + 10], S3[N + 10];
10 
11 int Tot[N + 10], n, L, k;
12 
13 bool Cmp(Node, Node);
14 bool Equal(Node, Node);
15 void CDQ(int, int);
16 void cdq(int, int);
17 
18 int main() {
19     scanf("%d%d", &n, &k);
20     for(int i = 1; i <= n; i ++)
21         scanf("%d%d%d", &S1[i].a, &S1[i].b, &S1[i].c);
22     std::sort(S1 + 1, S1 + 1 + n, Cmp);
23 
24     int Count = 0;
25     for(int i = 1; i <= n; i ++) {
26         ++Count;
27         if(!Equal(S1[i], S1[i + 1]))
28             S2[++L] = S1[i], S2[L].Cnt = Count, Count = 0;
29     } CDQ(1, L);
30     for(int i = 1; i <= L; i ++)
31         Tot[S2[i].Ans + S2[i].Cnt - 1] += S2[i].Cnt;
32     for(int i = 0; i < n; i ++) printf("%d\n", Tot[i]);
33     return 0;
34 
35 } bool Cmp(Node x, Node y) {
36     if(x.a != y.a) return x.a < y.a;
37     if(x.b != y.b) return x.b < y.b;
38     return x.c < y.c;
39 
40 } bool Equal(Node x, Node y) {
41     if(x.a != y.a) return false;
42     if(x.b != y.b) return false;
43     if(x.c != y.c) return false;
44     return true;
45 
46 } void CDQ(int l, int r) {
47     if(l >= r) return;
48     int Mid = (l + r) >> 1;
49     CDQ(l, Mid), CDQ(Mid + 1, r);
50     int p1, p2, nL;
51     p1 = nL = l, p2 = Mid + 1;
52     while(p1 <= Mid || p2 <= r)
53         if(p2 > r || p1 <= Mid && S2[p1].b <= S2[p2].b)
54             S2[p1].opt = 0, S1[nL++] = S2[p1++];
55         else S2[p2].opt = 1, S1[nL++] = S2[p2++];
56     for(int i = l; i <= r; i ++)
57         S2[i] = S3[i] = S1[i], S3[i].Loc = i;
58     cdq(l, r);
59 
60 } void cdq(int l, int r) {
61     if(l >= r) return;
62     int Mid = (l + r) >> 1;
63     cdq(l, Mid), cdq(Mid + 1, r);
64     int p1, p2, nL, Cnt;
65     p1 = nL = l, p2 = Mid + 1, Cnt = 0;
66     while(p1 <= Mid || p2 <= r)
67         if(p2 > r || p1 <= Mid && S3[p1].c <= S3[p2].c) {
68             if(!S3[p1].opt) Cnt += S3[p1].Cnt;
69             S1[nL++] = S3[p1++];
70             
71         } else {
72             if(S3[p2].opt) S2[S3[p2].Loc].Ans += Cnt;
73             S1[nL++] = S3[p2++];       
74         }
75     for(int i = l; i <= r; i ++) S3[i] = S1[i];
76 }

将就着看看吧……同机房神犇大部分人大括号换行, 我码风要被喷死了……

 

以上只是蒟蒻拙见, 感谢大佬的阅读。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!