The Preliminary Contest for ICPC Asia Nanjing 2019 ICPC南京站网络赛 I Washing clothes(李超树)

题目链接:https://nanti.jisuanke.com/t/41306

 

题目大意:有n个人,每个人到达的时间是t[i],每个人都得洗衣服,手洗的时间是y,机洗的时间是x,大家可以一起手洗但是同一时间只能有一个人机洗,问最后一个人洗完的最早时间

 

题目思路:首先可以注意到一点,如果每个人都是手洗的话,那么每个人停止的时间就是t[i]+y。假设我们已经知道了那个最早时间,那么假如有一个家伙手洗来不及了,那他想要完成任务唯一的可能就是去机洗,如果连这家伙都来不及了,那他后面的家伙就更来不及了,所以应该是,前面一部分人都在手洗,后面一部分都在机洗。假设一个人开始机洗,那么他的贡献就是从这一刻开始,后面的家伙都机洗,也就是max_{i\rightarrow j}t[i]+(n-i+1)*x就是从这家伙开始,后面的都能连着用洗衣机的情况的最大值求max。所以一个家伙对答案的贡献有两种可能,一种是t[i]+y,另一种是t[i]+(n-i+1)*x,肯定大家都想往小了走,所以这是一个分段函数,先递增然后保持不变,而且可以发现,转折点是y/(n-i+1),那么越靠后的人,斜率越小,转折点越靠后,所以只要一个人洗衣服他后面的家伙肯定都取得是洗衣服的情况,自动保证了这家伙洗衣服以后后面的人都在洗衣服,那么就把这些分段函数全插进去,然后在每个x求最大值就行了。

 

以下是代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN = 1e6+5;
int n,y;
int t[MAXN];
struct line{
    db k,b;
    int l,r,flag;
}a[MAXN<<1],k;
inline int get_id(int l,int r){return (l+r)|(l!=r);}
inline db calc(line a,int pos){return a.k*pos+a.b;}
void build(int l,int r){
    int rt=get_id(l,r);
    a[rt].k=a[rt].b=a[rt].l=a[rt].r=a[rt].flag=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(l,mid);build(mid+1,r);
}
void update(int l,int r,line k){
    int rt=get_id(l,r);
    if(k.l<=l&&r<=k.r){
        if(!a[rt].flag)a[rt]=k,a[rt].flag=1;
        else if(calc(k,l)>=calc(a[rt],l)&&calc(k,r)>=calc(a[rt],r))a[rt]=k;
        else if(calc(k,l)>calc(a[rt],l)||calc(k,r)>calc(a[rt],r)){
            int mid=(l+r)>>1;
            if(calc(k,mid)>calc(a[rt],mid)){
                swap(k,a[rt]);
            }
            if(calc(k,l)>calc(a[rt],l))update(l,mid,k);
            else update(mid+1,r,k);
        }
    }
    else{
        if(l==r)return;
        int mid=(l+r)>>1;
        if(k.l<=mid)update(l,mid,k);
        if(mid<k.r)update(mid+1,r,k);
    }
}
ll query(int l,int r,int x){
    int rt=get_id(l,r);
    if(l==r)return calc(a[rt],x);
    else {
        int mid=(l+r)>>1;
        ll ans=calc(a[rt],x);
        if(x<=mid)return max(ans,query(l,mid,x));
        else return max(ans,query(mid+1,r,x));
    }
}
int main(){
    while(~scanf("%d%d",&n,&y)){
        rep(i,1,n)scanf("%d",&t[i]);
        sort(t+1,t+n+1);
        build(1,y);
        rep(i,1,n){
            int pos=y/(n-i+1);
            k.l=1,k.r=pos,k.flag=1,k.k=n-i+1,k.b=t[i];
            update(1,y,k);
            k.l=pos+1,k.r=y,k.flag=1,k.k=0,k.b=t[i]+y;
            update(1,y,k);
        }
        rep(i,1,y){
            printf("%lld%c",query(1,y,i),i==y?'\n':' ');
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值