CDQ分治

CDQ分治

多维偏序问题
  • 二维偏序问题

    HDU 1541 Stars

    给定 n n n 个元素,第 i i i 个元素有 a i 、 b i a_i 、 b_i aibi 两个属性,设 f ( i ) f(i) f(i) 表示满足 a j ≤ a i a_j\leq a_i ajai b j ≤ b i b_j\leq b_i bjbi 的 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)=AxBx+AyBy。其中 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 的最大值。

对于另外三个方向,做法类似。例如左上方向,仍按照横坐标从小到大排序,树状数组改为维护

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值