权限题,无法传送%>_<%
Time Limit: 10 Sec Memory Limit: 128 MB
Description
一辆无限长的列车,有 k 个检票员,每个检票员一次检验 ai 个车厢,初始时所有检票员在 0 号车厢,列车长每次命令最靠左的编号最小的且能够继续检票的检票员向右走 ai 步,一共发出 n 个命令,输出每个售票员走的最后一步是列车长的第几次命令。
Input
第一行两个数 n,k(n<=2∗1013,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;
}