CDQ分治
多维偏序问题
-
二维偏序问题
HDU 1541 Stars
给定 n n n 个元素,第 i i i 个元素有 a i 、 b i a_i 、 b_i ai、bi 两个属性,设 f ( i ) f(i) f(i) 表示满足 a j ≤ a i a_j\leq a_i aj≤ai 且 b j ≤ b i b_j\leq b_i bj≤bi 的 j 的数量。
对于 d ∈ [ 0 , n ] d\in [0,n] d∈[0,n] ,求满足 f ( i ) = d f(i)=d f(i)=d 的数量。
首先,我们可以把两个元素抽象成一个点 ( a , b ) (a,b) (a,b) ,那么我们就是求一个矩形中有多少个点。 (1<=N<=15000, 0<=X,Y<=32000)
比如我们要求这个矩形内有多少个点:
方法一:按照x坐标排序,y坐标离散一下,从左向右跑一下树状数组插入,在第i个位置找一下在1~i-1位置上纵坐标小于i的纵坐标的点的个数。
-
用二维偏序CDQ解决树状数组问题
P3374 【模板】树状数组 1
在这篇博客里有很详细的讲解,说的很清晰。
通过这个题第一次理解了CDQ的查询的左右端点分开存储使用的神奇操作。
#include<iostream>
#include<cstdio>
#include<cstring>
#define MogeKo qwq
using namespace std;
const int maxn = 500005*3;
int n,m,cnt,cqry,opt,x,y,ans[maxn];
struct node{
int type,id,val;
bool operator < (const node & x) const{
if(id != x.id)return id < x.id;
else return type < x.type;
}
}q[maxn],tem[maxn];
void cdq(int L,int R){
if(L == R) return;
int mid = L+R>>1;
cdq(L,mid),cdq(mid+1,R);
int t1 = L,t2 = mid+1;
int sum = 0;
for(int i = L;i <= R;i++){
if( (t1 <= mid && q[t1]<q[t2]) || t2 > R){
if(q[t1].type == 1) sum += q[t1].val;
tem[i] = q[t1++];
}
else{
if(q[t2].type == 2) ans[q[t2].val] -= sum;
if(q[t2].type == 3) ans[q[t2].val] += sum;
tem[i] = q[t2++];
}
}
for(int i = L;i <= R;i++) q[i] = tem[i];
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++){
cnt++;
scanf("%d",&y);
q[cnt].type = 1;
q[cnt].id = i;
q[cnt].val = y;
}
for(int i = 1;i <= m;i++){
scanf("%d%d%d",&opt,&x,&y);
if(opt == 1){
q[++cnt].type = 1;
q[cnt].id = x;
q[cnt].val = y;
}
if(opt == 2){
cqry++;
q[++cnt].type = 2;
q[cnt].id = x-1;
q[cnt].val = cqry;
q[++cnt].type = 3;
q[cnt].id = y;
q[cnt].val = cqry;
}
}
cdq(1,cnt);
for(int i = 1;i <= cqry;i++)
printf("%d\n",ans[i]);
return 0;
}
-
三维偏序问题
天使玩偶
题目描述
Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。
我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 (x,y) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 (x,y) ,那么她离近的天使玩偶可能埋下的地方有多远。
因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为 d i s t ( A , B ) = ∣ A x − B x ∣ + ∣ A y − B y ∣ dist(A,B)=|Ax-Bx|+|Ay-By| dist(A,B)=∣Ax−Bx∣+∣Ay−By∣。其中 Ax 表示点 A的横坐标,其余类似。
输入格式
第一行包含两个整数n和m ,在刚开始时,Ayu 已经知道有n个点可能埋着天使玩偶, 接下来 Ayu 要进行m 次操作
接下来n行,每行两个非负整数 (xi,yi),表示初始n个点的坐标。
再接下来m 行,每行三个非负整数 t,xi,yi。
如果t=1 ,则表示 Ayu 又回忆起了一个可能埋着玩偶的点 (xi,yi) 。
如果t=2 ,则表示 Ayu 询问如果她在点 (xi,yi) ,那么在已经回忆出来的点里,离她近的那个点有多远
输出格式
对于每个t=2 的询问,在单独的一行内输出该询问的结果。
说明/提示
n,m<=300 000
xi,yi<=1 000 000
分析
先考虑较为简单的情况,只有t=2的情况。
首先把曼哈顿距离的绝对值去了,只要分成四种情况,左上,左下,右上,右下四种,都取一遍答案,再取最大值即可。
然后我们可以把n个点的坐标和m个询问的坐标一起按照横坐标从小到大排序,然后依次进行扫描:
若扫描到一个点 ( x i , y i ) (x_i,y_i) (xi,yi) ,则在树状数组或者线段树中把第y_i个位置上的值与 x i + y i x_i+y_i xi+yi 取max,同时维护前缀或区间最大值。
若扫描到一个询问(x,y),则在树状数组或者线段树中询问[0,y]上的最大值val。该询问左下方的答案就是 x+y-val。
在上面的做法中,排序满足了x_i≤x的条件,树状数组控制了y_i≤y的条件并维护了 x i + y i x_i+y_i xi+yi 的最大值。
对于另外三个方向,做法类似。例如左上方向,仍按照横坐标从小到大排序,树状数组改为维护