偏序基本思想有:树套树(代码量较大、不想写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;
}