寒假集训Day6 H(二分答案)

题目链接在这里:Problem - H - Codeforces

我们首先来看如果正着做的话,需要有一些高位的数降到0来补充,但是我们不知道哪几个高位的数需要降,如果每降一个高位来重新计算一下结果的话很显然是会超时的。我们可以发现,如果当前位的数要降的话,比它高的位也是要降的。这样我们可以二分这个要降的位,争取把每次查询的时间复杂度化简到O(N)的级别。

很显然我们能观察到这里哪一个数降不降和答案其实是等价的。

然后我们去考虑检验结果的问题。如果正着做的话我们会遇到一些困难,所以考虑倒着去做,由结果推到给定状态,也就是从结果位一个一个推到第一位,只要能推出是初始状态的子集就行了。如果当前位减一的话,相当于前面的每一位都要加一,这对前面的每一位都是等价的,所以说我们可以用一个变量去维护,然后往回推。可以发现,如果对于初始状态来说当前位一直是0的话,个数是以指数形式增长的,所以我们需要增加一个判断条件,如果当前位的个数超过了总共初始状态的1的个数就跳。

这里如果一个数>x 它就变成0;如果一个数小于x 它就会让比它小的每一个数都至少加1。

如果推到0以后多的位能补回到之前缺的位上,那么就是可行的,如果0多了,说明达到当前结果需要更多比当前结果大的数来变为0,所以是不可行的。

参考代码:

 

 1 #include "bits/stdc++.h"
 2 using namespace std;
 3 typedef long long LL;
 4 const int MAX=100105;
 5 LL n,a[MAX],b[MAX],mx;
 6 bool feasible(LL x){
 7     LL i,j;
 8     LL now,le,xx,yy;
 9     now=1,le=b[x];
10     for (i=x-1;i>=1;i--){
11         if (now>b[0]) return false;
12         if (now==a[i]) continue;
13         if (now<a[i]){
14             le+=a[i]-now;
15             continue;
16         }
17         if (now>a[i]){
18             now=now+(now-a[i]);
19             continue;
20         }
21     }
22     if (now-a[0]<=le) return true;
23     else return false;
24 }
25 int main(){
26 //    freopen ("h.in","r",stdin);
27 //    freopen ("h.out","w",stdout);
28     LL i,j;
29     LL low,high,mid,ans;
30     scanf("%lld",&n);
31     memset(a,0,sizeof(a));
32     for (i=0;i<n;i++) scanf("%lld",a+i);
33     b[n+1]=0;
34     for (i=n-1;i>=0;i--)
35         b[i]=a[i]+b[i+1];
36     if (b[0]==1){
37         if (a[0]==1 || a[1]==1) printf("1");
38         else{
39             i=2;
40             while (a[i]==0) i++;
41             printf("%lld",i);
42         }
43         return 0;
44     }
45     
46     low=1;high=100100ll;
47     while (low<=high){
48         mid=(low+high)>>1;
49         if (feasible(mid)){
50             ans=mid;
51             low=mid+1;
52         }
53         else high=mid-1;
54     }
55     printf("%lld",ans);
56     return 0;
57 }

 

上一篇:LeetCode1576. 替换所有的问号(Java版)


下一篇:Java寒假学习Day6:面向对象(1)