【杂题】bzoj3735[Pa2013]Konduktorzy

权限题,无法传送%>_<%

Time Limit: 10 Sec Memory Limit: 128 MB
Description
一辆无限长的列车,有 k 个检票员,每个检票员一次检验ai个车厢,初始时所有检票员在 0 号车厢,列车长每次命令最靠左的编号最小的且能够继续检票的检票员向右走ai步,一共发出 n 个命令,输出每个售票员走的最后一步是列车长的第几次命令。
Input
第一行两个数nk(n<=21013,k<=105,k<=n)
第二行 k 个数,表示每个检票员一次检验的长度ai(ai<=105)
Output
共一行 n 个数,第i个数表示售票员 i 走的最后一步是列车长的第几次命令。
Sample Input
10 3
3 5 6
Sample Output
10 9 7

如果 n 很小,只有105。那么完全可以用一个小根堆来模拟。
如果可以将 n 通过处理使得n变到 105 左右,就可以直接用上面的方法了。
于是乎,尝试用二分的方法确定每一个人至少要走到哪里。
为了防止出错,给每一个人都留出一步的空间,然后将他们集体向前走几步。统计一下剩下的步数,用堆模拟即可。

#include <iostream>
#include <cstdio>
#define MAXN 100005
#define LL long long int
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;

int n, num[MAXN];
LL m, l, r, mid, temp, ans[MAXN];

inline bool check(LL mid)
{
    LL t=m;
    for(int i=1;i<=n;++i){
        t-=mid/num[i];
        if(t<0)return 0;
    }
    return 1;
}

int mv[MAXN+MAXN];
LL cnt[MAXN];
inline void pushup(int&p)
{
    int &r=mv[p];
    r=p;
    if(cnt[mv[p<<1]]<cnt[r]||cnt[mv[p<<1]]==cnt[r]&&mv[p<<1]<r)r=mv[p<<1];
    if(cnt[mv[p<<1|1]]<cnt[r]||cnt[mv[p<<1|1]]==cnt[r]&&mv[p<<1|1]<r)r=mv[p<<1|1];
}

void update(int u,LL d)
{
    if(d<cnt[u])return;
    cnt[u]=d;
    for (;u;u>>=1)pushup(u);
}

char w;
inline void GET(int &t)
{
    do w=getchar();while(w<'0'||w>'9');
    do{t=t*10+w-'0';w=getchar();}while(w>='0'&&w<='9');
}

int main()
{
    cnt[0]=9223372036854775807ll;
    scanf("%lld",&m), GET(n);
    for(int i=1;i<=n;++i)
        GET(num[i]), l=max(l,num[i]);
    r=l*m, ++l;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(check(mid))temp=mid, l=mid+1;
        else r=mid-1;
    }

    LL t=temp;
    for(int i=1;i<=n&&t;++i)
        t=min(t,max((temp/num[i]-1)*num[i],0));
    LL used=0;
    int tmp;
    for(int i=1;i<=n;++i)
    {
        used+=t/num[i];
        update(i,t/num[i]*num[i]);
    }
    while(used<m)
    {
        tmp=mv[1];
        ans[tmp]=++used;
        update(tmp,cnt[tmp]+num[tmp]);
    }
    for(int i=1;i<n;++i)printf("%lld ",ans[i]);
    printf("%lld\n",ans[n]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值