1. 程式人生 > 實用技巧 >[2020多校聯考]糖果機器

[2020多校聯考]糖果機器

Solution

思路太妙,我想不到

一個位置的機器人能及時到達另一個位置去接糖果,當且僅當位置之差小於等於時間差,從 \(i\) 走到 \(j\) 用符號表示為

\[T_j-T_i\geq |S_j-S_i| \]

把絕對值拆掉

\[\begin{cases} T_j-T_i\geq S_j-S_i\\ T_j-T_i\geq S_i-S_j\ \end{cases}\]

再移項,把 \(i\)\(j\) 分別移到其中一邊

\[\begin{cases} T_j-S_j\geq T_i-S_i\\ T_j+S_j\geq T_i+S_i\ \end{cases}\]

將兩邊看成一個整體,將 \(T_x-S_x\)

當成橫座標,將 \(T_x+S_x\) 當成縱座標。若能從 \(i\) 走到 \(j\),那麼點 \((T_i-S_i,T_i+S_i)\) 一定在以點 \((T_j-S_j,T_j+S_j)\) 為右上角的矩形內。所以就轉換為了二維偏序。先按第橫座標為第一關鍵字縱座標為第二關鍵字排序之後,一個機器人能接住的糖就是點縱座標單調不降的一個子序列。使機器人個數最小化,實際上是用最少的沒有交集的單調不降子序列來覆蓋原序列。見導彈攔截

#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 100007

inline int read(){
	int x=0,flag=1; char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
	return flag? x:-x;
}

struct Node{
	int pos,t;
	int x,y;
	bool operator <(const Node X) const {
		if(x==X.x) return y<X.y;
		return x<X.x;
	}
}a[N];

int n,top=0,tag[N],sta[N];
int main(){
	freopen("candy.in","r",stdin);
	freopen("candy.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++){
		a[i].pos=read(),a[i].t=read();
		a[i].x=a[i].t+a[i].pos,a[i].y=a[i].t-a[i].pos;
	}
	sort(a+1,a+1+n);
	sta[++top]=a[1].y,tag[1]=1;
	for(int i=2;i<=n;i++){
		if(sta[top]>a[i].y){
			sta[++top]=a[i].y;
			tag[i]=top;
		}else{
			int l=1,r=top,ret=0;
			while(l<=r){
				int mid=(l+r)>>1;
				if(sta[mid]<=a[i].y){
					r=mid-1;
					ret=mid;
				}else l=mid+1;
			}
			if(!ret) ret=1;
			tag[i]=ret,sta[ret]=a[i].y;
		}
	}
	printf("%d\n",top);
	for(int i=1;i<=n;i++)
		printf("%d %d %d\n",a[i].pos,a[i].t,tag[i]);
}
/*
5
1 1
2 3
1 5
3 4
2 6
*/

Tips

至於縱座標為什麼也要升序,可以考慮在橫座標相等的情況下,縱座標小的可以轉移到縱座標大的,反之不行,所以升序更優。