1. 程式人生 > 實用技巧 >【YbtOJ#20236】紅點藍點

【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;
}