[2020多校联考]糖果机器

[2020多校联考]糖果机器

Solution

思路太妙,我想不到

一个位置的机器人能及时到达另一个位置去接糖果,当且仅当位置之差小于等于时间差,从 (i) 走到 (j) 用符号表示为

[T_j-T_igeq |S_j-S_i| ]

把绝对值拆掉

[egin{cases} T_j-T_igeq S_j-S_i\ T_j-T_igeq S_i-S_j end{cases}]

再移项,把 (i)(j) 分别移到其中一边

[egin{cases} T_j-S_jgeq T_i-S_i\ T_j+S_jgeq 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
",top);
    for(int i=1;i<=n;i++)
        printf("%d %d %d
",a[i].pos,a[i].t,tag[i]);
}
/*
5
1 1
2 3
1 5
3 4
2 6
*/

Tips

至于纵坐标为什么也要升序,可以考虑在横坐标相等的情况下,纵坐标小的可以转移到纵坐标大的,反之不行,所以升序更优。