正解复杂度 $ O(n*log2(n)) $,然而我打的是 $ O(n*log2(n)*log2(n)) $的。
50分暴力:
$ O(n*m) $暴力模拟喝水过程,不再赘述。
100分算法:
这中多过程题其实有个套路就是每次找出最先使状态变化的时刻,对于这个题来说就是找出那一个最先被喝到不能喝。
考虑用线段树维护(S-w[i])/a[i]+1即能喝的最多次数,这样就可以每次 $ O(1) $查出来啦。
设x为最小的次数,那么我们便可以先在 $ O(1) $的复杂度内把x的整圈转出来,代码实现如下:
m-=x/t[1].sum; ans+=x/t[1].sum*t[1].sum; x%=t[1].sum;
之后二分查找它的结束位置(因为中间有可能有不能再喝的)
L=pos+1,R=n; while(L<R) { int mid=(L+R)>>1; if(query(1,pos+1,mid)>=x) R=mid; else L=mid+1; }
(本算法的复杂度瓶颈就是这里)
但是我们发现,有可能根本没有用完所有的喝水次数,所以要再来一圈:
if(!pos) m--; if(x) { L=1,R=n; while(L<R) { int mid=(L+R)>>1; if(query(1,pos+1,mid)>=x) R=mid; else L=mid+1; } ans+=query(1,1,L); pos=L%n; }
注意这里m--的条件并不是x还有剩余,而是pos到了0(否则就可以得到WA80的好成绩。。。)
Dele(1,t[1].pos);
最后把这个水瓶删除即可。
关于循环外:
if(m) { ans+=t[1].sum*(m-1); for(int i=pos+1;i<=n;i++) { if(S>=w[i]+a[i]*ans) ans++; } }
因为有可能下一个水瓶用完是在m轮之后,所以要把m轮转完
最后就可以AC本题啦!在OJ上跑了3000ms(three_D大神跑了400ms)