BZOJ 2716 天使玩偶 CDQ分治
题意:
输入:
输出:
样例较长这里省略
思路:
算法:CDQ分治+树状数组
首先,题目中存在绝对值,肯定是去掉比较容易,所以第一种情况是考虑分类讨论,即将查询的点当做原点,然后分四个象限分别拆绝对值,但这个时候会发现其实每个象限的算法区别不大,因此另一种想法就是每次都将所有的转到第三象限中,即通过加减转换,此时对于需要查询的\(A\)点,玩偶的位置\(B\)到达的距离都可以直接变为\(dis=A_x+A_y-(B_x+B_y)\)由于\(A_x+A_y\)的值是固定的,需要dis最小,所以我们要找到\(B_y+B_x\)最大的点。
由于我们直接设定的是在第三个象限,所以找到\(B\)点存在限制\(B_y<=A_y\)和\(B_x<A_x\) 再加上一个时间限制,就是一个偏序问题。下面的思路是:先按照时间排序,在对\(x\)进行分治,用树状数组维护y的值。
因为这题数据很卡,下面是几个优化:
1、对于时间排序,每一次sort的复杂度是\(O(nlogn)\) 一共要进行四次,那么复杂度就直接炸了,但是实际上我们可以记下最开始的排列, 每一次更改变换直接用最开始的排列。
for(int i=1; i<=tot; i++)ans[i]=INF,P[i]=a[i];//这里的P数组就是用来记录初始序列
CDQ(1,tot);
for(int i=1; i<=tot; i++)P[i].x=Mx-P[i].x,a[i]=P[i];
2、在CDQ中\(x\)的排序用可以考虑归并
3、对于开始的\([1,n]\)个数只有加入,所以可以直接排序返回
if(r<=n){
sort(a+l,a+r+1);
return;
}
同时题目中还有个细节:\(x,y\)可能等于0,所以可以考虑都加一,防止树状数组陷入死循环。
附代码+注释:
#include<bits/stdc++.h>
#define M 500005
#define INF 1000000000
#define N 1000005
#define lowbit(x) (x&-x)
using namespace std;
int n,m,ans[M<<1],Mx;
void Rd(int &res) {
res=0;
char c;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c-'0');
while(c=getchar(),c>=48);
}
struct node {
int op,x,y,id;
bool operator<(const node&_)const {
if(x!=_.x)return x<_.x;
return y<=_.y;
}
} a[M<<1],p[M<<1],P[M<<1];
struct Tree {
int mx[N];
void Init() {
memset(mx,0,sizeof(mx));
}
void add(int x,int y) {
while(x<=Mx)mx[x]=max(mx[x],y),x+=lowbit(x);
}
int find(int x) {
int res=0;
while(x)res=max(res,mx[x]),x-=lowbit(x);
return res;
}
void clear(int x) {
while(x<=Mx)mx[x]=0,x+=lowbit(x);
}
} T;
void CDQ(int l,int r) {
if(r<=n){
sort(a+l,a+r+1);
return;
}
if(l>=r)return;
int mid=(l+r)>>1;
int L=l,R=mid+1;
int now=l;
CDQ(l,mid),CDQ(mid+1,r);//正常的归并排序过程中加入计算
while(L<=mid&&R<=r) {
if(a[L]<a[R]) {
if(a[L].op==1)T.add(a[L].y,a[L].x+a[L].y);
p[now++]=a[L++];
} else {
if(a[R].op==2) {
int t=T.find(a[R].y);
if(t)ans[a[R].id]=min(ans[a[R].id],a[R].x+a[R].y-t);//t>0表示存在这样的点 因为开始已经将x,y都加了1
}
p[now++]=a[R++];
}
}
while(R<=r) {
if(a[R].op==2) {
int t=T.find(a[R].y);
if(t)ans[a[R].id]=min(ans[a[R].id],a[R].x+a[R].y-t);
}
p[now++]=a[R++];
}
for(int i=l; i<L; i++)if(a[i].op==1)T.clear(a[i].y);//撤销
while(L<=mid)p[now++]=a[L++];
for(int i=l; i<=r; i++)a[i]=p[i];
}
int main() {
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
Rd(n),Rd(m);
for(int i=1; i<=n; i++)Rd(a[i].x),Rd(a[i].y),a[i].op=1,a[i].x++,a[i].y++,Mx=max(Mx,max(a[i].x,a[i].y)),a[i].id=i;
for(int i=n+1; i<=m+n; i++)Rd(a[i].op),Rd(a[i].x),Rd(a[i].y),a[i].x++,a[i].y++,Mx=max(Mx,max(a[i].x,a[i].y)),a[i].id=i;
int tot=n+m;Mx++;
T.Init();
for(int i=1; i<=tot; i++)ans[i]=INF,P[i]=a[i];//下面是对四个方向的计算
CDQ(1,tot);
for(int i=1; i<=tot; i++)P[i].x=Mx-P[i].x,a[i]=P[i];
CDQ(1,tot);
for(int i=1; i<=tot; i++)P[i].y=Mx-P[i].y,a[i]=P[i];
CDQ(1,tot);
for(int i=1; i<=tot; i++)P[i].x=Mx-P[i].x,a[i]=P[i];
CDQ(1,tot);
for(int i=1; i<=tot; i++)if(ans[i]!=INF)printf("%d\n",ans[i]);
return 0;
}