整体二分/按时间分治(cdq分治)

把这两个东西放在一起讲是因为思想很相同。

所谓分治就是把问题分而治之(?)

整体二分和按时间分治其实本质上差不多,核心思想就是:把问题划分为左右两部分,只要在划分的过程中统计完左部分对右部分的贡献,接下来在分开处理左右两部分的时候就不会受另一半的影响。  

先讲按时间分治(cdq分治)

按时间分治可以用cdq分治来解决。

cdq分治采用归并排序的思想,对于一个操作序列,保证它一开始按照时间有序。

然后我们可以通过统计左边操作对右边操作的影响,使得右边操作序列只关于自身有关,接下来只要分别处理左右两边的序列即可。

cdq分治可以用于处理高维偏序

对于偏序类问题,

一维排序,二维数据结构,三维分治,四维分治,高维暴力。

如何解决呢?

首先按照第一维排序,消除第一维影响

然后进行cdq分治,先递归处理左半边和右半边,

然后合并左右两边,按照第二维递增顺序,

由于右边已经处理完了,仅需考虑左边对右边询问的影响。

现在第一维和第二维都有序,只需要把左边的操作的第三维加入数据结构(通常是树状数组add(z, 1))进行维护即可。

P3810 【模板】三维偏序(陌上花开)

  1 #include <bits/stdc++.h>
  2 #define int int
  3 #define Mid ((l + r) >> 1)
  4 #define lson (rt << 1)
  5 #define rson (rt << 1 | 1)
  6 using namespace std;
  7 int read(){
  8     char c; int num, f = 1;
  9     while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
 10     while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';;
 11     return f * num;
 12 }
 13 const int N = 1e6 + 1009;
 14 struct node{
 15     int x, y, z, id, cnt;
 16 }a[N], tmp[N];
 17 bool operator ==(const node &a, const node &b) {
 18     return a.x == b.x && a.y == b.y && a.z == b.z;
 19 }
 20 int n, m, tot, ans[N], tt[N], tree[N];
 21 int ttt[N];
 22 bool cmp(node a, node b) {
 23     if(a.x == b.x && a.y == b.y) return a.z < b.z;
 24     if(a.x == b.x) return a.y < b.y;
 25     return a.x < b.x;
 26 }
 27 void add(int x, int y) {
 28     for( ; x <= m; x += x & -x)
 29         tree[x] += y;
 30 }
 31 int query(int x) {
 32     int ans = 0;
 33     for( ; x; x -= x & -x)
 34         ans += tree[x];
 35     return ans;
 36 }
 37 void cdq(int l, int r) {
 38     if(l == r) return ;
 39     cdq(l, Mid); cdq(Mid + 1, r);
 40     int i = l, j = Mid + 1, now = l - 1;
 41     while(i <= Mid && j <= r) {
 42         if(a[i].y <= a[j].y) {
 43             tmp[++now] = a[i];
 44             add(a[i].z, a[i].cnt);
 45             i++;
 46         } else {
 47             tmp[++now] = a[j];
 48             ans[a[j].id] += query(a[j].z);
 49             j++;
 50         }
 51     }
 52     while(i <= Mid) {
 53         tmp[++now] = a[i];
 54         add(a[i].z, a[i].cnt);
 55         i++;
 56     }
 57     while(j <= r) {
 58         tmp[++now] = a[j];
 59         ans[a[j].id] += query(a[j].z);
 60         j++;
 61     }
 62     for(int i = l; i <= Mid; i++) add(a[i].z, -a[i].cnt);
 63     for(int i = l; i <= r; i++) a[i] = tmp[i];
 64 }
 65 main()
 66 {
 67     n = read(); m = read();
 68     for(int i = 1; i <= n; i++) {
 69         a[i].x = read();
 70         a[i].y = read();
 71         a[i].z = read();
 72         a[i].cnt = 1;
 73     }
 74     sort(a + 1, a + 1 + n, cmp);
 75     for(int i = 1; i <= n; i++) {
 76         if(i == 1 || !(a[i] == a[i - 1])){
 77             a[++tot] = a[i];
 78         }else a[tot].cnt += a[i].cnt;
 79     }
 80     for(int i = 1; i <= tot; i++) a[i].id = i, ttt[i] = a[i].cnt;
 81     cdq(1, tot);
 82     for(int i = 1; i <= tot; i++) tt[ans[i] + ttt[i] - 1] += ttt[i];
 83     for(int i = 0; i < n; i++) printf("%d
", tt[i]);
 84     return 0;
 85 }
 86 
 87 /*
 88 10 3
 89 3 3 3
 90 2 3 3
 91 2 3 1
 92 3 1 1
 93 3 1 2
 94 1 3 1
 95 1 1 2
 96 1 2 2
 97 1 3 2
 98 1 2 1
 99 三维偏序
100 先按照第一维排序
101 然后对第二维归并
102 归并时计算左对右的贡献
103 先双指针,满足当前统计出的第二维都有序
104 维护一个树状数组
105 */
P3810 【模板】三维偏序(陌上花开)

然后是整体二分

整体二分引入了一个值域维度作为划分区间的标准,一个作用是动态求解区间k小/大值。

在二分的过程中,我们无法直接知道一个询问的答案,但是可以直接知道哪些操作不是询问的答案

对于修改操作,可以拆分成减去原数再加上现在的数。

二分过程就是对当前的值域进行划分,对于小于等于当前中点的修改操作,我们将其加入数据结构(维护它对[l,r]的一个贡献)。

对于一个询问,我们只需要查询它区间内操作对他的贡献是否大于它所求的k大。

如果大于,说明答案在左半边。

否则,消除左半边对他的影响后加入右半边。

例题:

P3834 【模板】可持久化线段树 2(主席树)

#include <iostream>
#include <cstdio>
#define int long long
#define Mid ((l + r) >> 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read(){
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';;
    return f * num;
}
const int N = 5e5 + 1009;
const int INF = 1e9;
struct node{
    int id, l, r, k;
}q[N], lq[N], rq[N];
int n, m, t, ans[N], tree[N];
void add(int x, int y) {
    for( ; x <= n; x += x & -x)
        tree[x] += y;
}
int query(int x) {
    int ans = 0;
    for( ; x; x -= x & -x)
        ans += tree[x];
    return ans;
}
void solve(int l, int r, int st, int ed) {
    if(st > ed) return ;
    if(l == r) {
        for(int i = st; i <= ed; i++) 
            if(q[i].id)
                ans[q[i].id] = l;
        return ;
    }
    int lt = 0, rt = 0;
    for(int i = st; i <= ed; i++) {
        if(q[i].id == 0) {
            if(q[i].r <= Mid) {
                lq[++lt] = q[i];
                add(q[i].l, 1);
            } else rq[++rt] = q[i];
        } else {
            int c = query(q[i].r) - query(q[i].l - 1);
            if(q[i].k <= c) lq[++lt] = q[i];
            else q[i].k -= c, rq[++rt] = q[i];
        }
    }
    for(int i = st; i <= ed; i++)
        if(q[i].id == 0 && q[i].r <= Mid)
            add(q[i].l, -1);
    for(int i = 1; i <= lt; i++) q[st + i - 1] = lq[i];
    for(int i = 1; i <= rt; i++) q[st + lt + i - 1] = rq[i];
    solve(l, Mid, st, st + lt - 1);
    solve(Mid + 1, r, st + lt, ed);
}
signed main()
{
    n = read(); m = read();
    for(int i = 1; i <= n; i++) {
        q[++t].id = 0;
        q[t].l = i;
        q[i].r = read();
    }
    for(int i = 1; i <= m; i++) {
        q[++t].id = i;
        q[t].l = read();
        q[t].r = read();
        q[t].k = read();
    }
    solve(-INF, INF, 1, t);
    for(int i = 1; i <= m; i++)
        printf("%d
", ans[i]);
    return 0;
}
P3834 【模板】可持久化线段树 2(主席树)

P2617 Dynamic Rankings

#include <iostream>
#include <cstdio>
#define int long long
#define Mid ((l + r) >> 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read(){
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';;
    return f * num;
}
const int N = 5e5 + 1009;
const int INF = 1e9;
struct node{
    int id, l, r, k, type;
}q[N], lq[N], rq[N];
int n, m, t, ans[N], tree[N], a[N];
void add(int x, int y) {
    for( ; x <= n; x += x & -x)
        tree[x] += y;
}
int query(int x) {
    int ans = 0;
    for( ; x; x -= x & -x)
        ans += tree[x];
    return ans;
}
void solve(int l, int r, int st, int ed) {
    if(st > ed) return ;
    if(l == r) {
        for(int i = st; i <= ed; i++) 
            if(q[i].id)
                ans[q[i].id] = l;
        return ;
    }
    int lt = 0, rt = 0;
    for(int i = st; i <= ed; i++) {
        if(q[i].id == 0) {
            if(q[i].r <= Mid) {
                lq[++lt] = q[i];
                add(q[i].l, q[i].type);
            } else rq[++rt] = q[i];
        } else {
            int c = query(q[i].r) - query(q[i].l - 1);
            if(q[i].k <= c) lq[++lt] = q[i];
            else q[i].k -= c, rq[++rt] = q[i];
        }
    }
    for(int i = 1; i <= lt; i++)
        add(lq[i].l, -lq[i].type);
    for(int i = 1; i <= lt; i++) q[st + i - 1] = lq[i];
    for(int i = 1; i <= rt; i++) q[st + lt + i - 1] = rq[i];
    solve(l, Mid, st, st + lt - 1);
    solve(Mid + 1, r, st + lt, ed);
}
signed main()
{
    int cnt = 0;
    n = read(); m = read();
    for(int i = 1; i <= n; i++) {
        q[++t].id = 0;
        q[t].l = i;
        q[i].r = read();
        q[i].type = 1;
        a[i] = q[i].r;
    }
    for(int i = 1; i <= m; i++) {
        char c;
        cin >> c;
        if(c == 'C') {
            int x, y;
            x = read(); y = read();
            q[++t].id = 0;
            q[t].l = x;
            q[t].r = a[x];
            q[t].type = -1;
            
            q[++t].id = 0;
            q[t].l = x;
            q[t].r = y;
            q[t].type = 1;    
            a[x] = y;
        } else {
            q[++t].id = ++cnt;
            q[t].l = read();
            q[t].r = read();
            q[t].k = read();
        }
    }
    solve(-INF, INF, 1, t);
    for(int i = 1; i <= cnt; i++)
        printf("%lld
", ans[i]);
    return 0;
}
P2617 Dynamic Rankings

P3332 [ZJOI2013]K大数查询

 1 #include <bits/stdc++.h>
 2 #define int long long
 3 #define Mid ((l + r) >> 1)
 4 #define lson (rt << 1)
 5 #define rson (rt << 1 | 1)
 6 using namespace std;
 7 int read(){
 8     char c; int num, f = 1;
 9     while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
10     while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
11     return f * num;
12 }
13 const int N = 2e5 + 1009;
14 const int inf = 0x3f3f3f3f;
15 struct node {
16     int l, r, x, id;
17 } q[N], lq[N], rq[N];
18 int n, m, ans[N], cnt;
19 int tree[N], tag[N];
20 void update(int rt) {
21     tree[rt] = tree[lson] + tree[rson];
22 }
23 void pushdown(int l, int r, int rt) {
24     if(!tag[rt]) return ;
25     tree[lson] += (Mid - l + 1) * tag[rt]; tag[lson] += tag[rt];
26     tree[rson] += (r - (Mid + 1) + 1) * tag[rt]; tag[rson] += tag[rt];
27     tag[rt] = 0;
28 }
29 void modify(int l, int r, int L, int R, int x, int rt) {
30     if(L <= l && r <= R) {
31         tree[rt] += x * (r - l + 1);
32         tag[rt] += x;
33         return ;
34     }
35     pushdown(l, r, rt);
36     if(L <= Mid) modify(l, Mid, L, R, x, lson);
37     if(Mid <  R) modify(Mid + 1, r, L, R, x, rson);
38     update(rt);
39 }
40 int query(int l, int r, int L, int R, int rt) {
41     if(L <= l && r <= R) return tree[rt];
42     pushdown(l, r, rt);
43     int ans = 0;
44     if(L <= Mid) ans += query(l, Mid, L, R, lson);
45     if(Mid <  R) ans += query(Mid + 1, r, L, R, rson);
46     return ans;
47 }
48 void solve(int lval, int rval, int l, int r) {
49     if(l > r || lval > rval) return ;
50     if(lval == rval) {
51         for(int i = l; i <= r; i++) 
52             if(q[i].id) 
53                 ans[q[i].id] = lval;
54         return ;
55     }
56     int mid = (lval + rval) / 2, lcnt = 0, rcnt = 0, fl = 0, fr = 0;
57     for(int i = l; i <= r; i++) {
58         if(q[i].id == 0) {
59             if(q[i].x > mid) {
60                 modify(1, n, q[i].l, q[i].r, 1, 1);
61                 lq[++lcnt] = q[i];
62             } else rq[++rcnt] = q[i];
63         } else {
64             int k = query(1, n, q[i].l, q[i].r, 1);
65             if(q[i].x <= k) lq[++lcnt] = q[i], fl = 1;
66             else q[i].x -= k, rq[++rcnt] = q[i], fr = 1;
67         }
68     }
69     for(int i = 1; i <= lcnt; i++)
70         if(lq[i].id == 0)
71             modify(1, n, lq[i].l, lq[i].r, -1, 1);
72     for(int i = 1; i <= lcnt; i++) q[l + i - 1] = lq[i];
73     for(int i = 1; i <= rcnt; i++) q[l + lcnt + i - 1] = rq[i];
74     if(fl) solve(mid + 1, rval, l, l + lcnt - 1);
75     if(fr) solve(lval, mid, l + lcnt, r);
76 }
77 signed main()
78 {
79     n = read(); m = read();
80     for(int i = 1; i <= m; i++) {
81         int opt = read(), x, y, c;
82         x = read(); y = read(); c = read();
83         if(opt == 1) 
84             q[i] = (node){x, y, c, 0};
85         else q[i] = (node) {x, y, c, ++cnt};
86     }
87     solve(-inf, inf, 1, m);
88     for(int i = 1; i <= cnt; i++)
89         printf("%lld
", ans[i]);
90     return 0;
91 }
92 /*
93 整体二分?
94 按照值域二分答案
95 
96 */
P3332 [ZJOI2013]K大数查询
原文地址:https://www.cnblogs.com/onglublog/p/14268659.html