时间限制:3秒 内存限制:128M
【问题描述】
为了写论文,Alex 经常要整理大量的数据。这一次,Alex 面临一个严峻的考验:他需要实现一个数据结构来维护一个点集。现在,二维平面上有N 个点。Alex 需要实现以下三种操作:
1. 在点集里添加一个点;
2. 给出一个点,查询它到点集里所有点的曼哈顿距离的最小值;
3. 给出一个点,查询它到点集里所有点的曼哈顿距离的最大值。
两个点的曼哈顿距离定义为它们的横坐标差的绝对值与纵坐标差的绝对值的和。这么困难的问题,Alex 当然不会做,只好再次请你帮忙了。
【输入格式】
第一行包含一个整数N,表示点集最初的点数。
接下来N 行,每行两个整数,依次表示每个点的横坐标和纵坐标。
第N+2 行包含一个整数Q,表示询问的数目。
接下来Q 行,每行三个整数,依次表示询问的类型,点的横坐标和纵坐标。0 类型表示添加一个点,1 类型表示查询到该点的曼哈顿距离的最小值,2 类型表示查询最大值。
【输出格式】
输出若干行,依次表示每个查询操作的答案。
【输入样例】
3
7 5
6 2
3 1
5
1 6 1
1 5 5
2 7 1
0 3 2
1 1 0
【输出样例】
1
2
4
3
【数据范围】
30%的数据:1 ≤ N, Q ≤ 1,000;
60%的数据:1 ≤ N, Q ≤ 40,000;
有30%的数据满足没有添加点的操作;
100%的数据:1 ≤ N, Q ≤ 100,000,点的坐标是不超过10^9的非负整数。
这道题开始做的时候打了个树套树,结果算了一下,又爆内存又超时,毕竟我用的线段树套线段树(其实套平衡树就不爆内存了)。
正解应该是用CDQ分治的思想。
关于CDQ的详细内容
我们可以直接把开始有的n个点看做是操作1,然后重要的就是合并的时候,前面对后面的影响了。
我们要求的是一个带绝对值的式子,我们可以分4种情况来去掉绝对值,去掉后我们就会发现每种情况都可以直接用一个树状数组来记录。
详细的记录方法见代码注释:
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=200005;
const ll inf=2000000000005ll;
struct shu
{
ll x,y,id,ans,name;
friend bool operator <(shu a,shu b)
{
if(a.x!=b.x) return a.x<b.x;
return a.y<b.y;
}
}a[maxn];
bool cmp(shu a,shu b){
return a.name<b.name;
}
ll bx[maxn],by[maxn],bit[maxn][2];
int totx,toty,n,m;
ll lowbit(ll x){
return x&(-x);
}
void in(ll x,ll t){
while(x<=toty)
{
bit[x][0]=min(bit[x][0],t);
bit[x][1]=max(bit[x][1],t);
x+=lowbit(x);
}
}
ll findmin(ll x){
ll ans=inf;
while(x)
{
ans=min(bit[x][0],ans);
x-=lowbit(x);
}
return ans;
}
ll findmax(ll x){
ll ans=-inf;
while(x)
{
ans=max(bit[x][1],ans);
x-=lowbit(x);
}
return ans;
}
void out(ll x){
while(x<=toty)
{
bit[x][0]=inf;
bit[x][1]=-inf;
x+=lowbit(x);
}
}
ll read(){
ll x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
void init()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i].x=read();a[i].y=read();a[i].id=0;
bx[i]=a[i].x;
by[i]=a[i].y;
a[i].name=i;
}
m=read();
for(int i=1;i<=m;i++)
{
a[i+n].id=read();a[i+n].x=read();a[i+n].y=read();
bx[i+n]=a[i+n].x;
by[i+n]=a[i+n].y;
if(a[i+n].id==1) a[i+n].ans=inf;
if(a[i+n].id==2) a[i+n].ans=-inf;
a[i+n].name=i+n;
}
sort(bx+1,bx+1+n+m);
sort(by+1,by+1+n+m);
totx=toty=1;
for(int i=2;i<=n+m;i++)
{
if(bx[i]!=bx[i-1]) bx[++totx]=bx[i];
if(by[i]!=by[i-1]) by[++toty]=by[i];
}
for(int i=1;i<=n+m;i++)
{
a[i].x=lower_bound(bx+1,bx+totx+1,a[i].x)-bx;
a[i].y=lower_bound(by+1,by+toty+1,a[i].y)-by;
}
for(int i=1;i<=toty;i++)
bit[i][0]=inf,bit[i][1]=-inf;
}
void work(int l,int r)
{
if(l==r||r<=n) return;
int m=(l+r)>>1;
work(l,m);
work(m+1,r);
sort(a+l,a+m+1);
sort(a+m+1,a+r+1);
//1 x1-x2+y1-y2
int t=l;
for(int i=m+1;i<=r;i++)if(a[i].id>0)
{
while(t<=m&&a[t].x<=a[i].x)
{
//记录x2+y2的值,我们保证了x2<=x1就直接在树状数组上查询y1前的值就可以了.
if(a[t].id==0) in(a[t].y,bx[a[t].x]+by[a[t].y]);
t++;
}
if(a[i].id==1) a[i].ans=min(a[i].ans,bx[a[i].x]+by[a[i].y]-findmax(a[i].y));
else a[i].ans=max(a[i].ans,bx[a[i].x]+by[a[i].y]-findmin(a[i].y));
}
t--;
while(t>=l)
{
if(a[t].id==0) out(a[t].y);
t--;
}
//2 x1-x2+y2-y1
t=l;
for(int i=m+1;i<=r;i++)if(a[i].id>0)
{
while(t<=m&&a[t].x<=a[i].x)
{
//记录x2-y2的值,这里的y2要大于y1,所以我们就用toty+1-y来当下表好用树状数组,剩下2种情况差不多,就留着读者体会了,不懂可以回复
if(a[t].id==0) in(toty+1-a[t].y,bx[a[t].x]-by[a[t].y]);
t++;
}
if(a[i].id==1) a[i].ans=min(a[i].ans,bx[a[i].x]-by[a[i].y]-findmax(toty+1-a[i].y));
else a[i].ans=max(a[i].ans,bx[a[i].x]-by[a[i].y]-findmin(toty+1-a[i].y));
}
t--;
while(t>=l)
{
if(a[t].id==0) out(toty+1-a[t].y);
t--;
}
//3
t=m;
for(int i=r;i>m;i--)if(a[i].id>0)
{
while(t>=l&&a[t].x>=a[i].x)
{
if(a[t].id==0) in(toty+1-a[t].y,bx[a[t].x]+by[a[t].y]);
t--;
}
if(a[i].id==1) a[i].ans=min(a[i].ans,findmin(toty+1-a[i].y)-bx[a[i].x]-by[a[i].y]);
else a[i].ans=max(a[i].ans,findmax(toty+1-a[i].y)-bx[a[i].x]-by[a[i].y]);
}
t++;
while(t<=m)
{
if(a[t].id==0) out(toty+1-a[t].y);
t++;
}
//4
t=m;
for(int i=r;i>m;i--)if(a[i].id>0)
{
while(t>=l&&a[t].x>=a[i].x)
{
if(a[t].id==0) in(a[t].y,bx[a[t].x]-by[a[t].y]);
t--;
}
if(a[i].id==1) a[i].ans=min(a[i].ans,findmin(a[i].y)-bx[a[i].x]+by[a[i].y]);
else a[i].ans=max(a[i].ans,findmax(a[i].y)-bx[a[i].x]+by[a[i].y]);
}
t++;
while(t<=m)
{
if(a[t].id==0) out(a[t].y);
t++;
}
}
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
init();
work(1,n+m);
sort(a+1,a+n+m+1,cmp);
for(int i=n+1;i<=n+m;i++)if(a[i].id>0)
printf("%d\n",(int)a[i].ans);
return 0;
}
再外送一个我的树套树做法,虽然什么都超,但如果没办法了还是可以用的,毕竟还是有70分的。
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=200005;
const int inf=2000000005;
struct shu
{
int x,y,id;
}a[maxn];
int lc[maxn*2],rc[maxn*2],cnt=0,root[maxn*2],root1=0;
int lt[maxn*60],rt[maxn*60],cnt2=0;
ll minv[maxn*60][2],maxv[maxn*60][2];
ll bx[maxn],by[maxn];
int n,m,totx,toty;
void buid(int &now,int l,int r){
now=++cnt;
if(l==r) return;
int m=(l+r)>>1;
buid(lc[now],l,m);
buid(rc[now],m+1,r);
}
void up(int now){
for(int i=0;i<2;i++)
{
minv[now][i]=min(minv[lt[now]][i],minv[rt[now]][i]);
maxv[now][i]=max(maxv[lt[now]][i],maxv[rt[now]][i]);
}
}
void add(int &now,int l,int r,int x,int y){
if(!now) now=++cnt2;
if(l==r)
{
minv[now][0]=min(minv[now][0],bx[x]+by[y]);
maxv[now][0]=max(maxv[now][0],bx[x]+by[y]);
minv[now][1]=min(minv[now][1],bx[x]-by[y]);
maxv[now][1]=max(maxv[now][1],bx[x]-by[y]);
return;
}
int m=(l+r)>>1;
if(y<=m) add(lt[now],l,m,x,y);
else add(rt[now],m+1,r,x,y);
up(now);
}
void in(int now,int l,int r,int x,int y){
add(root[now],1,toty,x,y);
if(l==r) return;
int m=(l+r)>>1;
if(x<=m) in(lc[now],l,m,x,y);
else in(rc[now],m+1,r,x,y);
}
ll read(){
ll x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
void init(){
n=read();
for(int i=1;i<=n;i++)
{
a[i].x=read();a[i].y=read();
bx[i]=a[i].x;
by[i]=a[i].y;
}
m=read();
for(int i=1;i<=m;i++)
{
a[i+n].id=read();a[i+n].x=read();a[i+n].y=read();
bx[i+n]=a[i+n].x;
by[i+n]=a[i+n].y;
}
sort(bx+1,bx+1+n+m);
sort(by+1,by+1+n+m);
totx=toty=1;
for(int i=2;i<=n+m;i++)
{
if(bx[i]!=bx[i-1]) bx[++totx]=bx[i];
if(by[i]!=by[i-1]) by[++toty]=by[i];
}
for(int i=1;i<=n+m;i++)
{
a[i].x=lower_bound(bx+1,bx+totx+1,a[i].x)-bx;
a[i].y=lower_bound(by+1,by+toty+1,a[i].y)-by;
}
buid(root1,1,totx);
for(int i=0;i<maxn*60;i++)
{
maxv[i][0]=maxv[i][1]=-inf;
minv[i][0]=minv[i][1]=inf;
}
}
int max0(int now,int l,int r,int x,int y,int id){
if(!now||l>=y)
{
if(id) return maxv[now][0];
return minv[now][0];
}
int m=(l+r)>>1;
ll t1,t2;
if(id) t1=t2=-inf;
else t1=t2=inf;
if(m>=y) t2=max0(lt[now],l,m,x,y,id);
t1=max0(rt[now],m+1,r,x,y,id);
if(id) return max(t1,t2);
return min(t1,t2);
}
int find0max(int now,int l,int r,int x,int y,int id){
if(l>=x) return max0(root[now],1,toty,x,y,id);
int m=(l+r)>>1;
ll t1,t2;
if(id) t1=t2=-inf;
else t1=t2=inf;
if(m>=x) t2=find0max(lc[now],l,m,x,y,id);
t1=find0max(rc[now],m+1,r,x,y,id);
if(id) return max(t1,t2);
return min(t1,t2);
}
int max1(int now,int l,int r,int x,int y,int id){
if(!now||r<=y)
{
if(id) return maxv[now][1];
return minv[now][1];
}
int m=(l+r)>>1;
ll t1,t2;
if(id) t1=t2=-inf;
else t1=t2=inf;
t2=max1(lt[now],l,m,x,y,id);
if(m<y) t1=max1(rt[now],m+1,r,x,y,id);
if(id) return max(t1,t2);
return min(t1,t2);
}
int find1max(int now,int l,int r,int x,int y,int id){
if(l>=x) return max1(root[now],1,toty,x,y,id);
int m=(l+r)>>1;
ll t1,t2;
if(id) t1=t2=-inf;
else t1=t2=inf;
if(m>=x) t2=find1max(lc[now],l,m,x,y,id);
t1=find1max(rc[now],m+1,r,x,y,id);
if(id) return max(t1,t2);
return min(t1,t2);
}
ll min0(int now,int l,int r,int x,int y,int id){
if(!now||r<=y)
{
if(!id) return maxv[now][0];
return minv[now][0];
}
int m=(l+r)>>1;
ll t1,t2;
if(!id) t1=t2=-inf;
else t1=t2=inf;
t2=min0(lt[now],l,m,x,y,id);
if(m<y) t1=min0(rt[now],m+1,r,x,y,id);
if(!id) return max(t1,t2);
return min(t1,t2);
}
ll find0min(int now,int l,int r,int x,int y,int id){
if(r<=x) return min0(root[now],1,toty,x,y,id);
int m=(l+r)>>1;
ll t1,t2;
if(!id) t1=t2=-inf;
else t1=t2=inf;
t2=find0min(lc[now],l,m,x,y,id);
if(m<x) t1=find0min(rc[now],m+1,r,x,y,id);
if(!id) return max(t1,t2);
return min(t1,t2);
}
ll min1(int now,int l,int r,int x,int y,int id){
if(!now||l>=y)
{
if(!id) return maxv[now][1];
return minv[now][1];
}
int m=(l+r)>>1;
ll t1,t2;
if(!id) t1=t2=-inf;
else t1=t2=inf;
if(m>=y) t2=min1(lt[now],l,m,x,y,id);
t1=min1(rt[now],m+1,r,x,y,id);
if(!id) return max(t1,t2);
return min(t1,t2);
}
ll find1min(int now,int l,int r,int x,int y,int id){
if(r<=x) return min1(root[now],1,toty,x,y,id);
int m=(l+r)>>1;
int t1,t2;
if(!id) t1=t2=-inf;
else t1=t2=inf;
t2=find1min(lc[now],l,m,x,y,id);
if(m<x) t1=find1min(rc[now],m+1,r,x,y,id);
if(!id) return max(t1,t2);
return min(t1,t2);
}
int find(int x,int y,int id)
{
ll t1,t2,t3,t4,t,tt;
t1=find0max(root1,1,totx,x,y,id)-bx[x]-by[y];
t2=find1max(root1,1,totx,x,y,id)-bx[x]+by[y];
t3=bx[x]+by[y]-find0min(root1,1,totx,x,y,id);
t4=bx[x]-by[y]-find1min(root1,1,totx,x,y,id);
/*if(id) t=-1;
else t=inf;
if(t1<0||t1>inf) t1=t;
if(t2<0||t2>inf) t2=t;
if(t3<0||t3>inf) t3=t;
if(t4<0||t4>inf) t4=t;*/
if(id) return max(max(t1,t2),max(t3,t4));
return min(min(t1,t2),min(t3,t4));
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
init();
for(int i=1;i<=n;i++)
in(root1,1,totx,a[i].x,a[i].y);
for(int i=n+1;i<=n+m;i++)
{
if(a[i].id==0) in(root1,1,totx,a[i].x,a[i].y);
if(a[i].id==1) printf("%d\n",find(a[i].x,a[i].y,0));
if(a[i].id==2) printf("%d\n",find(a[i].x,a[i].y,1));
}
return 0;
}