BZOJ3735 : [Pa2013]Konduktorzy
二分一个最大的位置$x$,计算$t=\sum_{i=1}^k\lfloor\frac{x}{a_i}\rfloor$。
如果$t\leq n$,那么说明就算全部检票员都走到了这里,也不够$n$个指令,所以可以先将所有检票员尽量向$x$位置走,并将用掉的指令数扣除。
然后将$x$适当往前调整,使得每个检票员还差至少一步。
因为$a_i$互不相同,并且$a_i\leq 100000$,所以剩余指令数并不多,用堆直接模拟即可。
时间复杂度$O(k\log^2k)$。
#include<cstdio> #include<algorithm> #include<queue> #define N 100010 using namespace std; typedef long long ll; typedef pair<ll,int> P; int n,i,a[N];ll m,L,R,mid,fin,now,ans[N];priority_queue<P,vector<P>,greater<P> >Q; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} bool check(ll x){ ll t=m; for(int i=1;i<=n;i++){ t-=x/a[i]; if(t<0)return 0; } return 1; } int main(){ scanf("%lld",&m);read(n); for(i=1;i<=n;i++){ read(a[i]); if(a[i]>R)R=a[i]; } L=R+1,R*=m; while(L<=R)if(check(mid=(L+R)>>1))L=(fin=mid)+1;else R=mid-1; for(R=fin,i=1;i<=n;i++)R=min(R,max((fin/a[i]-1)*a[i],0LL)); for(i=1;i<=n;i++)now+=R/a[i],Q.push(P(R/a[i]*a[i],i)); while(now<m){ P t=Q.top();Q.pop(); ans[t.second]=++now; t.first+=a[t.second]; Q.push(t); } for(i=1;i<n;i++)printf("%lld ",ans[i]);printf("%lld",ans[n]); return 0; }