【YbtOJ#20236】红点蓝点
题目
题目链接:https://www.ybtoj.com.cn/contest/66/problem/4
思路
我们将询问拆成四个问题,那每一个蓝点在红点右上方为例,那么此时 \(|x_b-x_r|+|y_b-y_r|=(x_b+y_b)-(x_r+y_r)\)。
然后我们可以通过旋转和翻转让四个询问都转化为蓝点在红点右上方,然后跑四次即可。下文通过翻转将两点纵坐标差不超过 \(d\) 转换为两点横坐标差不超过 \(d\)。
对于每一个询问,我们将蓝点和红点分别按照纵坐标从大到小排序,然后对于一个蓝点 \((x,y)\),它可以贡献到的红点 \((x',y')\) 一定,满足 \(0\leq x-x'\leq d\),且 \(y'<y\)。
然后就变成了线段树单点修改区间取最小值的模板了。
时间复杂度 \(O(n\log n)\)。
代码为了方便,没有选择离散化,而是在 \([1,10^8+5]\) 建动态开点线段树。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010,LG=28,MAXN=N*LG*4,Inf=1e9;
int n,m,rt,ans[N];
struct node
{
int x,y,id;
}a[N],b[N];
bool cmp(node x,node y)
{
return x.y>y.y;
}
void flip()
{
for (int i=1;i<=n;i++)
swap(a[i].x,a[i].y),swap(b[i].x,b[i].y);
}
void rotate()
{
for (int i=1;i<=n;i++)
{
swap(a[i].x,a[i].y); a[i].y=1e8+5-a[i].y;
swap(b[i].x,b[i].y); b[i].y=1e8+5-b[i].y;
}
}
struct SegTree
{
int tot,lc[MAXN],rc[MAXN],minn[MAXN];
int update(int x,int l,int r,int k,int v)
{
if (!x) x=++tot;
minn[x]=min(minn[x],v);
if (l==k && r==k) return x;
int mid=(l+r)>>1;
if (k<=mid) lc[x]=update(lc[x],l,mid,k,v);
else rc[x]=update(rc[x],mid+1,r,k,v);
return x;
}
int query(int x,int l,int r,int ql,int qr)
{
if (!x) return Inf;
if (l==ql && r==qr) return minn[x];
int mid=(l+r)>>1;
if (qr<=mid) return query(lc[x],l,mid,ql,qr);
if (ql>mid) return query(rc[x],mid+1,r,ql,qr);
return min(query(lc[x],l,mid,ql,mid),query(rc[x],mid+1,r,mid+1,qr));
}
}seg;
void solve()
{
rt=seg.tot=0;
memset(seg.lc,0,sizeof(seg.lc));
memset(seg.rc,0,sizeof(seg.rc));
memset(seg.minn,0x3f3f3f3f,sizeof(seg.minn));
sort(a+1,a+1+n,cmp);
sort(b+1,b+1+n,cmp);
for (int i=1,j=1;i<=n;i++)
{
for (;j<=n && b[j].y>=a[i].y;j++)
rt=seg.update(rt,1,1e8+5,b[j].x,b[j].x+b[j].y);
int s=seg.query(1,1,1e8+5,a[i].x,min(a[i].x+m,(int)1e8+5));
if (s<Inf && s-a[i].x-a[i].y<ans[a[i].id])
ans[a[i].id]=s-a[i].x-a[i].y;
}
}
int main()
{
freopen("portal.in","r",stdin);
freopen("portal.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
a[i].id=i; a[i].x++; a[i].y++;
}
for (int i=1;i<=n;i++)
{
scanf("%d%d",&b[i].x,&b[i].y);
b[i].id=i; b[i].x++; b[i].y++;
}
memset(ans,0x3f3f3f3f,sizeof(ans));
flip(); solve();
flip(); rotate(); solve();
rotate(); flip(); solve();
flip(); rotate(); solve();
for (int i=1;i<=n;i++)
if (ans[i]<Inf) printf("%d\n",ans[i]);
else printf("0\n");
return 0;
}