BZOJ 2716: [Violet 3]天使玩偶 | CDQ分治
题目:
南开OJ有非权限提交处
http://oi.nks.edu.cn/zh/Problem/Details/2739
题解:
鹅鹅鹅....有三维(t,x,y),所以可以用CDQ解决的好题
初始点就是t超级小的点
先按x排个序,时间作为分治的第二维,这样只有前半段时间的修改操作对后半段时间的询问操作需要在当前solve处理,y作为需要用数据结构维护的第三维
考虑把曼哈顿距离展开变成没有绝对值的式子,但是四种情况不好维护.
如果以某一次询问的点(x0,y0)为源点,显然答案一定在四个象限中的一个
考虑只维护第三象限的答案,那么绝对值式子可以变成(x0+y0)-(x+y)
用树状数组可以维护y0位置之前的前缀最大值
这样每次修改操作就在y位置插入(x+y)就可以做到维护位于第三象限的答案
其他象限的答案我们只要旋转坐标轴变成第三象限即可
#include<cstdio> #include<algorithm> #include<cstring> #define N 1000010 #define INF 0x7fffffff using namespace std; int n,m; struct node { int id,op,x,y,pos; bool operator < (const node &b) const { if (x!=b.x) return x<b.x; if (y!=b.y) return y<b.y; return op<b.op; } }q[N],tmp[N]; int ans[N],tot,t[2*N],lim=N; void insert(int x,int w) { for (;x<=lim;x+=x&-x) t[x]=max(t[x],w); } int query(int x) { int ret=0; for (;x;x-=x&-x) ret=max(ret,t[x]); return ret; } void clear(int x) { for (;x<=lim;x+=x&-x) t[x]=0; } void solve(int l,int r) { if (l==r) return ; int mid=l+r>>1; for (int i=l;i<=r;i++) { if (q[i].id<=mid && q[i].op==1) insert(q[i].y,q[i].y+q[i].x); if (q[i].id>mid && q[i].op==2) { int t=query(q[i].y); if (t!=0) ans[q[i].pos]=min(ans[q[i].pos],q[i].x+q[i].y-t); } } for (int k=l,i=l,j=mid+1;k<=r;k++) { if (q[k].id<=mid && q[k].op==1) clear(q[k].y); if (q[k].id<=mid) tmp[i++]=q[k]; else tmp[j++]=q[k]; } for (int i=l;i<=r;i++) q[i]=tmp[i]; solve(l,mid); solve(mid+1,r); } int main() { scanf("%d%d",&n,&m); for (int i=1,x,y;i<=n;i++) { scanf("%d%d",&x,&y); q[i]=(node){i,1,x+1,y+1,0}; } for (int i=1,op,x,y;i<=m;i++) { scanf("%d%d%d",&op,&x,&y); if (op==1) q[i+n]=(node){i+n,1,x+1,y+1,0}; else q[i+n]=(node){i+n,2,x+1,y+1,++tot},ans[tot]=INF; } sort(q+1,q+1+n+m); solve(1,n+m); for (int i=1;i<=n+m;i++) q[i].y=lim-q[i].y; sort(q+1,q+1+n+m);solve(1,n+m); for (int i=1;i<=n+m;i++) q[i].x=lim-q[i].x; sort(q+1,q+1+n+m);solve(1,n+m); for (int i=1;i<=n+m;i++) q[i].y=lim-q[i].y; sort(q+1,q+1+n+m);solve(1,n+m); for (int i=1;i<=tot;i++) printf("%d\n",ans[i]); return 0; }