偏序问题

偏序基本思想有:树套树(代码量较大、不想写qaq)、CDQ分治。


CDQ分治:

用一个区间 [L, R] 表示序列,递归处理把它分成左右两个序列 [L, M] 和 [M+1, R],并起来的时候用左边的问题来解决右边的问题。主要特点在:一个问题对另一个问题产生了一定的影响。


偏序问题:

一维偏序
一维偏序,我不认为这个叫偏序,一维的情况可以直接排序来解决问题。

二维偏序
二维偏序,给出 N 个数对 (a, b),求对于每个数对 (a, b) 有多少对 (x, y) 满足 a > x 且 b > y. 引用:https://www.cnblogs.com/mlystdcall/p/6219421.html
从归并排序求逆序对中引出这个问题。
归并排序求逆序对的时候实际上可以认为每个单位是有两个元素,一个元素是单位的序号,一个元素是单位的值,而第一个元素–序号是已经排好序的。在并的过程中(升序),有两个指针 i、j,i 作用于左区间,j 作用于右区间,给定 j 在左区间内查找比 ar[j] 大的 ar[i],那么第 i 个及其之后的单位实际上在原序列中在第 j 个之前,那么这些都是逆序对(ar[j] 应当放在 ar[i] 之前),那么这就是“这个元素为结尾的逆序数对数”应当加上“在左区间中比这个元素的个数”。把这个问题想成二维偏序来做就是 a 是位置作为第一元素,b 是值作为第二元素,每个单位 (a, b) 找到有多少 (x, y) 满足 a > x 且 b < y。
那么真正意义上的二维偏序就是,按照第一个元素升序排序之后,归并求顺序对的对数问题。和归并排序求逆序对的对数意思是一致的。

三维偏序
给定 N 个有三个元素的单位 (a, b, c),求对于每个单位 (a, b, c),有多少个单位 (x, y, z)满足 x <= a 且 y <= b 且 z <= c。
实际上这个问题类似二维偏序,对第一维排序,然后递归 CDQ 分治,在合并的时候对于 x 来说,左区间的 x 都是小于右区间的 x,因此 x 是不产生影响的,然后左区间和右区间都进行排序操作,两个指针 i、j 指向左右区间,对右区间的一个 j.y 来说,遍历左区间的 i.y,如果 i.y 比 j.y 要小的话,就把它放进树状数组中,如果找到了一个 i.y > j.y,那么这个 i 之前的所有的 x 都小于 j 的 x,而且 i 之前的所有的 y 也都小于 j 的 y,由于之前把 z 都放入的树状数组,那么查找比 j.z 要小的有多少个,这就是递归中的复合要求的部分。
步骤就是:第一维排序,第二维 CDQ 分治,第三维 树状数组。
注意:使用完之后记得清空树状数组。


例题:
SDNU.1252
http://www.acmicpc.sdnu.edu.cn/problem/show/1252

1252.H.强哥要置你于死地

Description
自从用了LJP药剂,小城老师的学生们都发奋学习,取得了优异的成绩。这让小城老师又开心,又无奈。开心的是再也不用担心学生的成绩跟不上了,无奈的是学生们都努力学习,他竟然闲的不自在了。于是在工作之余,他本着学习的态度,体验了一下腾讯的绝地求生刺激战场,但是人菜还爱冲锋的他总是早早成盒,总是被双排的黄老师一顿埋怨。
“一定是我选枪选的不好,而不是我技术菜”他如是想到。于是他总结了N把枪的射速,弹夹容量和精准度(这三个数值当然是越大越好了),让你来帮他比较一下,这N把枪中的每一把能完胜几把枪(完胜的意思是三个数据都不比另一把小)。

Input
第一行输入一个正整数N(N<=100000)代表枪的个数,接下来N行,每行三个正整数X,Y,Z分别代表枪的三个属性。(1<=X,Y,Z<=100000)

Output
一行数据代表每把枪能完胜的枪的个数,两个数之间用空格隔开

Sample Input
5
7 6 4
7 5 1
1 3 6
1 2 1
9 8 10

Sample Output
2 1 1 0 4

这是一个裸的三维偏序题。

代码:

#include 
#include 
#include 
#include 
using namespace std;

struct poi {
    int x;
    int y;
    int z;
    int num;
};

poi ar[100005];
int bit[1000050];
int ans[100005];

int n;
int nd;

int lowbit (int x) {
    return x&(-x);
}

void add (int x, int ad) {
    while (x <= nd) {
        bit[x] += ad;
        x += lowbit(x);
    }
}

int sum (int x) {
    int sum = 0;
    while (x) {
        sum += bit[x];
        x -= lowbit(x);
    }
    return sum;
}

bool cmp1 (poi a, poi b) {
    if (a.x != b.x) {
        return a.x < b.x;
    }
    else {
        if (a.y != b.y) {
            return a.y < b.y;
        }
        else {
            return a.z < b.z;
        }
    }
}

bool cmp2 (poi a, poi b) {
    if (a.y != b.y) {
        return a.y < b.y;
    }
    else {
        return a.z < b.z;
    }
}

void cdq (int l, int r) {
    if (l == r) {
        return ;
    }
    int m = (l+r)>>1;
    cdq(l, m);
    cdq(m+1, r);
    sort(ar+l, ar+m+1, cmp2);
    sort(ar+m+1, ar+r+1, cmp2);
    int i = l, j = m + 1;
    while (j <= r) {
        while (i <= m && ar[i].y <= ar[j].y) {
            add(ar[i].z, 1);
            i++;
        }
        ans[ar[j].num] += sum(ar[j].z);
        j++;
    }
    for (int k = l; k < i; k++) {
        add(ar[k].z,-1);
    }
}

int main () {
    scanf("%d", &n);
    nd = -1;
    for (int i = 0; i < n; i++) {
        int x, y, z;
        scanf("%d %d %d", &x, &y, &z);
        ar[i].x = x;
        ar[i].y = y;
        ar[i].z = z;
        nd = max(nd, x);
        nd = max(nd, y);
        nd = max(nd, z);
        ar[i].num = i;
    }
    sort(ar, ar+n, cmp1);
/*
    cout << "nd : " << nd << endl << endl;
    for (int i = 0; i < n; i++) {
        cout << i << ".x:" << ar[i].x << " " << i << ".y:" << ar[i].y << " " << i << ".z:" << ar[i].z << endl;
    }
    cout << endl;
*/
    cdq(0, n-1);
    for (int i = 0; i < n-1; i++) {
        printf("%d ", ans[i]);
    }
    printf("%d\n", ans[n-1]);
    return 0;
}

你可能感兴趣的:(分治)