[李超树or想法]2019ICPC南京网络赛 I washing clothes

https://nanti.jisuanke.com/t/41306

题意:n个人分别在ti时间来到洗衣房洗衣服,每个人可以选择洗衣机洗和手洗,手洗花费y时间,机洗花费x时间,只有一台洗衣机,所以多个人想用要排队。现在要求对于每一个在[1,y]范围内的x,输出所有人洗完衣服所需要的最小时间。

如果有错误欢迎指正,这道题第一种做法是看到这个博客才知道的:

https://blog.csdn.net/TDD_Master/article/details/100374149#commentsedit

但是始终不明白为什么不需要考虑n-p这个点的情况,所以我的代码还是考虑了,也是A了哒。

对于p的分析是我自己的理解,可能会有错误(目前自我感觉还挺对咳咳

如果有错误,欢迎指正!!!

分析:

假设t_{i}<=t_{i+1}

首先,机洗时间一定<=手洗时间,所以最后一个人肯定可以是机洗啦。另外如果第i个人选择手洗,则他前面的人没有选择机洗的必要,因为这个人手洗肯定贼慢,还是由这个人影响最后的答案。所以可以知道,假设从第x个人开始选择机洗比较快,那后面的人都是机洗,前面的人可以假装都是手洗。

假设从第i个人开始机洗,i个人机洗的时间就是max{ tj+(n-j+1)*x } (i<=j<=n)

答案是min \left \{ max\left ( t_{i-1}+y,max\left \{ t_{j}+(n-j+1)*x \right \} \right ) \right \} (1<=i<=n,i<=j<=n)

就是下面绿绿的那条的最低点。注意最低点的横坐标不一定是整数咳咳。

假设最后p个人选择机洗的时候,手洗时间恰好开始>=机洗时间([i,n]机洗)

则此时,

f_{i}>=g_{i}>=t_{i}+px

p<= \frac{g_{i}-t_{i}}{x} <=\frac{f_{i}-t_{i}}{x}

又因为f_{i}=t_{i-1}+y ,所以p<=\frac{t_{i-1}+y-t_{i}}{x}<=\frac{y}{x} (因为t_{i-1}<t_{i}

意思就是确定x的情况下,最多最多y/x个人用洗衣机

那么从n往前for到i-1(就是点n-p),维护max{ tj+(n-j+1)*x },咳反正就是可以暴力求答案了啦

分析一下复杂度,就是y*(\frac{1}{1+1}+\frac{1}{2+1}+...+\frac{1}{y-1+1})

差不多是ylogy叭

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e6+6;
const ll INF=8e18;
ll n,y,a[maxn];
ll ans[maxn];
int main(){
    while(~scanf("%lld%lld",&n,&y)){
        for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
        sort(a+1,a+n+1);
        a[0]=-y;
        ll low=n,g;
        for(ll x=y;x>=1;x--){
            int p=y/x;
            ll maxx=a[n]+x,res=max(a[n-1]+y,maxx);
            for(int j=n-1;j>=n-p&&j;j--){
                maxx=max(maxx,a[j]+(n-j+1)*x);
                res=min(res,max(a[j-1]+y,maxx));
            }
            ans[x]=res;
        }
        for(ll i=1;i<=y;i++) printf("%lld%c",ans[i],i==y?'\n':' ');
    }
    return 0;
}

李超树的话就和题解上说的差不多啦

有学弟是用下凸包写的后面部分emmm并不知道具体是怎么实现的

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e6+6;
const ll INF=8e18;
ll n,y,a[maxn];
double Intersection(double k1,double b1,double k2,double b2){return 1.0*(b2-b1)/(k1-k2);}
ll segb[maxn<<2],segk[maxn<<2];
bool did[maxn<<2];
#define lson (t<<1)
#define rson (t<<1|1)
void update(ll t,ll l,ll r,ll k,ll b){
    if(!did[t]) segb[t]=b,segk[t]=k,did[t]=1;
    else{
        ll f1=k*l+b,f2=segk[t]*l+segb[t],f3=k*r+b,f4=segk[t]*r+segb[t];
        if(f1<=f2&&f3<=f4) return;
        else if(f1>=f2&&f3>=f4) segk[t]=k,segb[t]=b;
        else{
            ll mid=l+r>>1;
            double len=Intersection((double)k,(double)b,(double)segk[t],(double)segb[t]);
            if(f1>=f2){
                if(len<=mid) update(lson,l,mid,k,b);
                else{
                    update(rson,mid+1,r,segk[t],segb[t]);
                    segk[t]=k;segb[t]=b;
                }
            }else{
                if(len>mid) update(rson,mid+1,r,k,b);
                else{
                    update(lson,l,mid,segk[t],segb[t]);
                    segk[t]=k;segb[t]=b;
                }
            }
        }
    }
}
ll query(ll t,ll l,ll r,ll x){
    ll ans=0;
    if(did[t]) ans=max(ans,segk[t]*x+segb[t]);
    if(l==r){
        return ans;
    }
    ll mid=l+r>>1;
    if(x<=mid) return max(ans,query(lson,l,mid,x));
    else return max(ans,query(rson,mid+1,r,x));
}
ll ans[maxn];
int main(){
    while(~scanf("%lld%lld",&n,&y)){
        memset(did,0,sizeof(did));
        for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
        sort(a+1,a+n+1);
        a[0]=-y;
        ll low=n,g;
        update(1,1,y,1,a[n]);
        for(ll x=y;x>=1;x--){
            ans[x]=INF;
           // printf(" x=%d\n",x);
            while(low>=1){
                g=query(1,1,y,x);
               // printf(" low=%lld g=%lld\n",low,g);
                if(g<=a[low-1]+y){
                    ans[x]=min(ans[x],max(g,a[low-1]+y)),low--;
                    update(1,1,y,n-low+1,a[low]);
                   // printf(" add x=%d low=%lld k=%lld b=%lld\n",x,low,n-low+1,a[low-1]);
                }else{
                    ans[x]=min(ans[x],max(g,a[low-1]+y));
                    if(x<y) ans[x]=min(ans[x],ans[x+1]);
                    break;
                }
            }
        }
        for(ll i=1;i<=y;i++) printf("%lld%c",ans[i],i==y?'\n':' ');
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值