Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 数论

  • 动态规划

  • 暴力

  • 图论

  • 数据结构

  • 字符串

  • 几何

  • 博弈论

  • 其他

    • 01分数规划学习笔记
    • CDQ分治学习笔记
      • 算法说明
      • 例题 Codeforces 12D
    • wqs二分学习笔记
    • 一两句话小技巧
    • 单调性应用学习笔记
    • 哈希函数(Hash)函数的设计
    • 带权并查集处理线段
    • 环检测算法学习笔记
    • 约瑟夫环学习笔记
    • 组合情况
    • 高效位运算

CDQ分治学习笔记

# 算法说明

CDQ分治是一种用来处理多维偏序的好方法

对于3维偏序,一般可以这样处理

  1. 第一维直接sort排序
  2. 第二维二分归并排序
  3. 第三维使用低复杂度的数据结构(树状数组等)

# 例题 Codeforces 12D

CDQ分治并不比标程慢,就是细节比较多,而且代码量有点令人作呕....

由于题目要求严格小于,需要对CDQ进行一些改造。

  1. 对aaa从小到大排序
  2. 在进行二分时,由于是严格小于,因而如果要产生贡献,必须有∀aleft,arightaleft<aright\forall a_{left},a_{right} \ a_{left} < a_{right}∀aleft​,aright​aleft​<aright​
  3. 在合并的时候,要从后往前遍历,判断左半部分的人是否会suicide,如果会就给他打上标记

为了保证2,我们需要分类讨论一下

  1. 如果a[from]=a[to]a[from]=a[to]a[from]=a[to],这一部分不可能产生贡献,因而只需要将其按bbb排序即可
  2. 否则,我们要寻找一个分界点midmidmid,使得对于∀ai,aj(from≤i≤mid,mid<j≤to),ai<aj\forall a_i,a_j(from\le i \le mid,mid \lt j \le to), a_i<a_j∀ai​,aj​(from≤i≤mid,mid<j≤to),ai​<aj​,注意细节即可(哭了,细节比想法重要!!)
#include <bits/stdc++.h>
#define ll long long
#define Android ios::sync_with_stdio(false), cin.tie(NULL)

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 5e5 + 1000;

struct val {
    int a, b, c, idx;
} vals[maxn], tmp[maxn];

bool cmp1(const val & v1, const val & v2){
    if(v1.a == v2.a) {
        if(v1.b == v2.b) return v1.c < v2.c;
        else return v1.b < v2.b;
    }
    return v1.a < v2.a;
}

bool tag[maxn] = {0};
int va[maxn]={0}, _time[maxn]={0};
int dfn = -1, greatest;
vector<int> a, a_pos;

int query(int p) {
    int ret = 0;
    for(int i = p; i > 0; i -= (-i)&i) {
        if(_time[i] != dfn) _time[i] = dfn, va[i] = 0;
        ret += va[i];
    }
    return ret;
}

void add(int p, int delta) {
    for(int i = p; i <= greatest; i += (-i)&i){
        if(_time[i] != dfn) _time[i] = dfn, va[i] = 0;
        va[i] += delta;
    }
}

void cdq(int from, int to, bool contribute) {
    if(from >= to) return;
    int mid = (from + to) >> 1;
    if(!contribute || vals[from].a == vals[to].a){ // 保证左侧一定小于右侧
        contribute = false;
    }else{
        int m1 = lower_bound(a.begin(), a.end(), vals[mid].a + 1) - a.begin();
        if(a_pos[m1]-1 >= to){
            mid = a_pos[m1-1]-1; 
        }else{
            mid = a_pos[m1]-1;
        }
    }
    cdq(from, mid, contribute), cdq(mid + 1, to, contribute);

    dfn++;
    int l = mid, r = to, k = to;
    while(l >= from && r > mid) {
        if(vals[r].b > vals[l].b) {
            if(contribute) add(vals[r].c, 1);
            tmp[k--] = vals[r--];
        } else {
            if(contribute) if(query(greatest) - query(vals[l].c) > 0) tag[vals[l].idx] = true;
            tmp[k--] = vals[l--];
        }
    }
    while(l >= from) {
        if(contribute) if(query(greatest) - query(vals[l].c) > 0) tag[vals[l].idx] = true;
        tmp[k--] = vals[l--];
    }
    while(r > mid) tmp[k--] = vals[r--];
    for(int i = from; i <= to; i++) vals[i] = tmp[i];
}

void solve() {
    int n, ans=0;
    cin >> n;
    for(int i = 1; i <= n; i++)  cin >> vals[i].a, vals[i].idx = i;
    for(int i = 1; i <= n; i++)  cin >> vals[i].b;
    // 对c离散化(因为要用树状数组)
    vector<int> unq(n + 1);
    for(int i = 1; i <= n; i++)  {
        cin >> vals[i].c;
        unq[i] = ++vals[i].c; // 树状数组从1开始
    }
    sort(unq.begin(), unq.end());
    unq.resize(unique(unq.begin(), unq.end()) - unq.begin());
    greatest = unq.size() + 3;
    for(int i=1; i<=n; i++) vals[i].c = lower_bound(unq.begin(), unq.end(), vals[i].c) - unq.begin();

    vals[0] = {-1, -1, -1, -1};
    sort(vals + 1, vals + n + 1, cmp1);
    for(int i=1; i<=n; i++){
        if(vals[i].a != vals[i-1].a) {
            a.push_back(vals[i].a);
            a_pos.push_back(i);
        }
    }
    a.push_back(inf);
    a_pos.push_back(n+1);

    cdq(1, n, true);
    for(int i=1; i<=n; i++) if(tag[i]) ans++;
    
    cout << ans << endl;
}

signed main() {
    Android;
    solve();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
上次更新: 2021/02/24, 03:37:30
01分数规划学习笔记
wqs二分学习笔记

← 01分数规划学习笔记 wqs二分学习笔记→

最近更新
01
深入理解Java虚拟机
03-04
02
DNS工作原理
02-27
03
改用VuePress啦
02-23
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式