一、基于时间的分治算法 --------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