题意:
输入:
输出:
样例较长这里省略
思路:
算法: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; }
来源:https://www.cnblogs.com/cly1231/p/10983225.html