cdq分治

CDQ解决三维偏序

为了方便处理时共用一个大概模板

我们都指定 需要被统计的元素作为 归并时 后半部分 也就是说我们指定 需要被统计元素为归并排序(从小到大)中 较大者 

 

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

首先按第一个属性 sort 可以解开第一种属性的约束关系

再去归并排序第二个属性, 树状数组类似扫描线统计答案 

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 2e5 + 100;

template <typename T>
struct BIT {
    T tr[N];
    int n;
    void init(int n_){
        n = n_;
        for(int i = 0; i <= n; i ++) tr[i] = 0;
    }
    int lowbit(int x) {return x & -x;}
    void add(int x, T v){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
    }
    T sum(int x){
        T res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }
    T sum(int l, int r) {return sum(r) - sum(l - 1);}
    void add(int l, int r, T v) {add(l, v); add(r + 1, -v);};
};

array<int, 5> a[N], t[N];
BIT<int> T;
int ans[N];

void cdq(int l, int r) {
    if (l == r) return;
    int mid = (l + r) >> 1;
    cdq(l, mid); cdq(mid + 1, r);
    int p1 = l, p2 = mid + 1, p3 = 0;
    while (p1 <= mid || p2 <= r) {
        if (p2 > r || (p1 <= mid && a[p1][1] <= a[p2][1])) {
            T.add(a[p1][2], a[p1][3]);
            t[p3++] = a[p1++];
        } else {
            a[p2][4] += T.sum(a[p2][2]);
            t[p3++] = a[p2++];
        }
    }
    for (int i = l; i <= mid; i++) T.add(a[i][2], -a[i][3]);
    for (int i = 0; i < p3; i++) a[l + i] = t[i];
}

void solve() {
    int n, k; cin >> n >> k;
    T.init(k + 10);
    for (int i = 0; i < n; i++) {
        int x, y, z; cin >> x >> y >> z;
        a[i] = {x, y, z, 1, 0};
    }
    sort(a, a + n);
    int p = 0;
    for (int i = 0; i < n; i++) {
        if (p != 0 && (a[i][0] == a[p - 1][0] && a[i][1] == a[p - 1][1] && a[i][2] == a[p - 1][2])) {
            a[p - 1][3]++;
        } else {
            a[p++] = a[i];
        }
    }

    cdq(0, p - 1);  

    for (int i = 0; i < p; i++) ans[a[i][3] + a[i][4] - 1] += a[i][3];  
    for (int i = 0; i < n; i++) cout << ans[i] << endl;
}   

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();

    return 0;   
}
View Code

 

简单题 - BZOJ 2683

这题给出来的样例是错的, 瞪了半天...

我们对时间作为第一个属性排个序, 然后扫描线维护

对于查询操作我们通过二维前缀和转换成四个矩形, 扫描线扫就行

#include <bits/stdc++.h>
using namespace std;
#define endl "\n";
#define int long long
typedef long long ll;

const int N = 1e6 + 100;

template <typename T>
struct BIT {
    T tr[N];
    int n;
    void init(int n_){
        n = n_;
        for(int i = 0; i <= n; i ++) tr[i] = 0;
    }
    int lowbit(int x) {return x & -x;}
    void add(int x, T v){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
    }
    T sum(int x){
        T res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }
    T sum(int l, int r) {return sum(r) - sum(l - 1);}
    void add(int l, int r, T v) {add(l, v); add(r + 1, -v);};
};

array<int, 4> a[N * 4], t[N * 4];
int ans[N];
BIT<int> T;

void cdq(int l, int r) {
    if (l == r) return;
    int mid = l + r >> 1;
    cdq(l, mid); cdq(mid + 1, r);
    int p1 = l, p2 = mid + 1, p3 = 0;
    while (p1 <= mid || p2 <= r) {
        if (p2 > r || (p1 <= mid && a[p1][0] <= a[p2][0])) {
            if (a[p1][3] == 0) T.add(a[p1][1], a[p1][2]);
            t[p3++] = a[p1++];
        } else {
            if (a[p2][3] != 0) ans[a[p2][2]] += a[p2][3] * T.sum(a[p2][1]);
            t[p3++] = a[p2++];
        }
    }
    for (int i = l; i <= mid; i++) {
        if (a[i][3] == 0) T.add(a[i][1], -a[i][2]);
    }
    for (int i = 0; i < p3; i++) a[l + i] = t[i];
}

void solve() {
    int n; cin >> n;
    int t = 0, q = 0;
    T.init(n + 10);
    while (true) {      
        int op; cin >> op;
        if (op == 3) break;
        else if (op == 1) {
            int x, y, v; cin >> x >> y >> v;
            a[++t] = {x, y, v, 0};
        } else {
            int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
            q++;
            a[++t] = {x2, y2, q, 1};
            a[++t] = {x1 - 1, y1 - 1, q, 1};
            a[++t] = {x2, y1 - 1, q, -1};
            a[++t] = {x1 - 1, y2, q, -1};
        }
    }
    cdq(1, t);
    for (int i = 1; i <= q; i++) cout << ans[i] << endl;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code

 

P3157 [CQOI2011] 动态逆序对

先放个代码

#include <bits/stdc++.h>
using namespace std;
#define endl "\n";
#define int long long
typedef long long ll;

const int N = 2e5 + 100;

template <typename T>
struct BIT {
    T tr[N];
    int n;
    void init(int n_){
        n = n_;
        for(int i = 0; i <= n; i ++) tr[i] = 0;
    }
    int lowbit(int x) {return x & -x;}
    void add(int x, T v){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
    }
    T sum(int x){
        T res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }
    T sum(int l, int r) {return sum(r) - sum(l - 1);}
    void add(int l, int r, T v) {add(l, v); add(r + 1, -v);};
};

int a[N], t[N], pos[N];
BIT<int> T;

vector<int> cdq(vector<array<int, 4>> a) {
    int n = a.size();
    vector<int> ans(n + 1, 0);
    sort(a.begin(), a.end());
    vector<array<int, 4>> tmp = a;

    function<void(int, int)> cdq = [&](int l, int r) {
        if (l == r) return;
        int mid = l + r >> 1;
        cdq(l, mid); cdq(mid + 1, r);
        int p1 = l, p2 = mid + 1, p3 = 0;
        while (p1 <= mid || p2 <= r) {
            if (p2 > r || (p1 <= mid && a[p1][1] <= a[p2][1])) {
                T.add(a[p1][2], 1);
                tmp[p3++] = a[p1++];
            } else {
                ans[a[p2][3]] += T.sum(a[p2][2]);
                tmp[p3++] = a[p2++];
            }
        }
        for (int i = l; i <= mid; i++) T.add(a[i][2], -1);
        for (int i = 0; i < p3; i++) a[i + l] = tmp[i];
    };
    
    cdq(0, n - 1);
    // cout << "TEST" << endl;
    // for (auto i : ans) cout << i << endl;
    return ans;
}

void solve() {
    int n, m; cin >> n >> m;
    T.init(n + 100);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        pos[a[i]] = i;
    }
    for (int i = 1; i <= m; i++) {
        int x; cin >> x;
        t[pos[x]] = i;
    }

    int cnt = m + 1;
    for (int i = 1; i <= n; i++) {
        if (!t[i]) t[i] = cnt++;
    }    
    vector<array<int, 4>> tmp;
    for (int i = 1; i <= n; i++) {
        tmp.push_back({n + 1 - t[i], i, n + 1 - a[i], i});
    }
    vector<int> ans(n + 2, 0);
    auto ans1 = cdq(tmp);
    for (int i = 1; i <= n; i++) ans[t[i]] = ans1[i];
    tmp.clear();
    for (int i = 1; i <= n; i++) {
        tmp.push_back({n + 1 - t[i], n + 1 - i, a[i], i});
    }
    auto ans2 = cdq(tmp);
    for (int i = 1; i <= n; i++) ans[t[i]] += ans2[i];
    for (int i = n; i >= 1; i--) ans[i] += ans[i + 1];
    for (int i = 1; i <= m; i++) cout << ans[i] << endl;
}   

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T; cin >> T;
    // while (T--) solve();
    solve();

    return 0;
}
View Code

 

cdq优化 dp 转移

似乎由于 dp 转移是有一个从左到右的过程那么我们 cdq 分治不能写传统 cdq 分治

得先处理左边,再处理中间,最后处理右边

posted @ 2024-01-19 22:32  zhujio  阅读(1)  评论(0编辑  收藏  举报