离线分治算法:

本文介绍了两种分治算法:基于时间的CDQ分治算法,通过实例解析了如何解决天使玩偶的问题;另一种是基于值域的整体分治算法,以K-th Number和ZOJ 2112问题为例,展示了如何处理区间第K小数的查询和修改操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、基于时间的分治算法 --------CDQ分治
例题:AcWing 254. 天使玩偶
Ayu在七年前曾经收到过一个天使玩偶,当时她把它当做时间囊埋在了地下。

而七年后的今天,Ayu却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。

我们把Ayu生活的小镇看做一个二维平面直角坐标系,而Ayu会不定时的记起可能在某个点(x,y)埋下了天使玩偶。

或者Ayu会询问你,假如她在(x,y),那么她离最近的天使玩偶可能埋下的地方有多远。

因为Ayu只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为曼哈顿距离:

dist(A,B)=|Ax−Bx|+|Ay−By|
其中Ax表示点A的横坐标,其余类似。

输入格式
第一行包含两个整数n和m,在刚开始时,Ayu已经知道有n个点可能埋着天使玩偶,接下来Ayu要进行m次操作。

接下来n行,每行两个非负整数xi,yi,表示初始n个点的坐标。

再接下来m行,每行三个非负整数 t,x,y 。

如果t=1,表示Ayu又回忆起了一个可能埋着玩偶的点(x,y)。

如果t=2,表示Ayu询问如果她在坐标(x,y),那么在已经回忆出的点里,离她最近的那个点有多远。

输出格式
对于每个t=2的询问,在单独的一行内输出该询问的结果。

数据范围
n,m≤5∗105,坐标范围为 0~106。

输入样例:
2 3
1 1
2 3
2 1 2
1 3 3
2 4 2
输出样例:
1
2

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define ll long long
#define llu unsigned ll
using namespace std;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int maxn=1000100;
struct node
{
   
    int pos,x,y;
};
node a[maxn];//原始问题的操作序列,长度为n+m
node b[maxn];//静态问题的坐标,按横坐标排序,及其在a中的下标
int c[maxn],tot;//树状数组,坐标的最大范围
int ans[maxn],n,m,t;

bool operator <(const node &a,const node &b)
{
   
    if(a.x!=b.x) return a.x<b.x;
    else return a.y<b.y;
}

int ask(int x)
{
   
    int ans=-inf;
    for(;x;x-=(x&-x))
        ans=max(ans,c[x]);
    return ans;
}

void add(int x,int y)
{
   
    for(x;x<tot;x+=(x&-x)) c[x]=max(c[x],y);
}

// 求解简化版问题,需要考虑b[st~ed]的坐标,根据4个方向的不同,
// 横坐标顺序为de(±1),树状数组维护的信息用系数dx,dy(±1)指定

void cal(int st,int ed,int de,int dx,int dy)
{
   
    for(int i=st;i!=ed;i+=de)
    {
   
        int y=(dy==1?b[i].y:tot-b[i].y);
        int temp=dx*b[i].x+dy*b[i].y;
        if(a[b[i].pos].pos==1) add(y,temp);
        else ans[b[i].pos]=min(ans[b[i].pos],abs(temp-ask(y)));
    }
    //撤销修改。
    for(int i=st;i!=ed;i+=de)
    {
   
        int y=(dy==1?b[i].y:tot-b[i].y);
        if(a[b[i].pos].pos==1)
            for(int j=y;j<tot;j+=(j&-j)) c[j]=-inf;
    }
}



void cdq_div(int l,int r)
{
   
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(l<=mid) cdq_div(l,mid);
    if(mid+1<=r) cdq_div(mid+1,r
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值