UVALive 7501 Business Cycle(二分)题解
题意:n个数,有一个起始值,按顺序从第一个开始不断循环取数,如果取完后相加小于0就变为0,最多取p个数,问你得到大于等于值g所需要的最小起始值为多少
思路:这题目爆long long爆的毫无准备,到处都有可能爆值。
显然,我们能想出,初始值越大,那么走相同步数所得到的数字就会越大(或相等),那么我们就可以用二分法每次判断是否能得到g值,大概logG * n * C的复杂度。那么现在问题就是怎么判定初始值s是否能得到g值。
我们可以求循环两次的结果差dis = tot2 - tot1,来判断每次循环的走向是增还是减或者不变。之所以算两次循环是因为可能遇到这样的样例:
4 5 10
1 2 -100 20
假设我初始值为10,那么第一次循环后得到20,那么其实不是一次循环就会增长10,因为第二次循环后还是20,所以要循环两次判断走势。
p <= 2n 或者 dis <= 0直接遍历两次循环就行了; p > 2n时,我们就让他一直循环,然后遍历最后一圈+ mod(就是p % n)。为什么不是只遍历最后的mod,而是要提前一圈遍历?因为有可能我在最后一圈里可能遇到在最后几个数中出现了-INF的超小值,我吃了这个超小值之后就不划算了。
最后注意别随时有可能爆long long...orz
代码:
#include<set> #include<map> #include<cmath> #include<queue> #include<string> #include<cstdio> #include<cstring> #include<sstream> #include<algorithm> typedef long long ll; using namespace std; const int maxn = 1e5 + 10; const int MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; ll v[maxn]; ll n, g, p; bool getMax(ll st){ if(st >= g) return true; if(p <= n){ ll tot = st; for(ll i = 0; i < p; i++){ tot += v[i]; if(tot < 0) tot = 0; if(tot >= g) return true; } return false; } else{ //p > n ll tot = st; for(ll i = 0; i < n; i++){ tot += v[i]; if(tot < 0) tot = 0; if(tot >= g) return true; } ll last = tot; if(p <= 2 * n){ for(ll i = 0; i < p - n; i++){ tot += v[i]; if(tot < 0) tot = 0; if(tot >= g) return true; } return false; } else{ //p > 2n for(ll i = 0; i < n; i++){ tot += v[i]; if(tot < 0) tot = 0; if(tot >= g) return true; } ll dis = tot - last; if(dis <= 0) return false; else{ ll k = p / n, rest; k--; if(k >= g / dis) return true; //防止爆long long rest = p - k * n; tot = last + dis * (k - 1LL); if(tot >= g) return true; for(ll i = 0; i < n; i++){ tot += v[i]; if(tot < 0) tot = 0; if(tot >= g) return true; } rest -= n; for(ll i = 0; i < rest; i++){ tot += v[i]; if(tot < 0) tot = 0; if(tot >= g) return true; } return false; } } } } int main(){ int t, ca = 1; scanf("%d", &t); while(t--){ scanf("%lld%lld%lld", &n, &g, &p); for(ll i = 0; i < n; i++){ scanf("%lld", &v[i]); } ll l = 0, r = g, ans = g; while(l <= r){ ll m = (l + r) / 2; if(getMax(m)){ r = m - 1; ans = m; } else{ l = m + 1; } } printf("Case #%d: %lld\n", ca++, ans); } return 0; }