[ZOJ 4062][2018ICPC青岛站][Plants vs. Zombies]
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4062
题目大意:给一个大小为n的数组,数组编号从1到n,每一个元素的值代表每经过一次这个位置,这个位置就加上这个元素的值,所有位置最初的值都是0,最初从0开始移动m次,求最终所有位置的最小值的最大值是多少.(n<1e5,m<1e12,a[i]<1e5)
题解:二分+贪心,每个位置尽量往右弹跳(因为之前的位置都满足了条件,只需要尽可能让右边的大即可.),注意各种细节即可.
(1)int/int的时候向0取整,而不是向下取整(所以要小心负数/正数的情况)
(2)要避免乘法爆long long ,比如对于先乘后除的式子先除后乘,同时当sum>m时直接return false,避免继续加下去爆long long (现场赛也是因为这个地方爆long long 没有了牌子..)
1 #include<iostream> 2 #include<cstring> 3 #include<cmath> 4 #include<map> 5 #include<cstdio> 6 #include<algorithm> 7 using namespace std; 8 typedef long long ll; 9 ll n,m; 10 ll a[100005]; 11 bool check(ll x){ 12 ll sum=0; 13 ll res=0; 14 for(int i=0;i<n;i++){ 15 ll xx=(x/a[i]+(x%a[i]!=0)-res); 16 if((x/a[i])+(x%a[i]!=0)<=res){if(i!=n-1)xx=1;else xx=0;} 17 res=xx-1; 18 if(res<0)res=0; 19 sum+=xx+res; 20 if(sum>m)return false; 21 } 22 return true; 23 } 24 int main(){ 25 int t; 26 cin>>t; 27 while(t--){ 28 scanf("%lld%lld",&n,&m); 29 ll maxx=0; 30 for(int i=0;i<n;i++){ 31 scanf("%lld",&a[i]); 32 maxx=max(maxx,a[i]); 33 } 34 ll r=1e18; 35 ll l=0; 36 ll ans=0; 37 while(l<=r) 38 { 39 ll mid=(l+r)>>1; 40 if(check(mid)) 41 { 42 ans=mid; 43 l=mid+1; 44 }else{ 45 r=mid-1; 46 } 47 } 48 printf("%lld\n",ans); 49 } 50 return 0; 51 }