[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

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

posted @ 2020-11-30 21:08  Kreap  阅读(64)  评论(0编辑  收藏  举报