【2018.10.1】「JOI 2014 Final」年轮蛋糕

题面

一看到求“最小值的最大值”这种问题,就能想到二分了。

二分答案,然后我们要把一圈分成三块,使这三块的大小都$\geq mid$。做法是把环展开成2倍长度的链,先钦定一个起点,然后根据前缀和再二分一下前两块的最小大小(注意前两块要连着),第三块用一圈的大小减去前两块的大小即可得到。如果第三块的大小$\geq mid$就返回$true$,提高答案范围;否则返回$false$,降低答案范围。

这样就能卡着最优情况下最小那一块的最大值从而得出答案了。

上面这种做法是$O(n*log_n*log_a)$,且二分次数多,常数较大,比较卡时。能不能不二分前两块的最小大小而快速求出?

如果做过“不超过某数的最大区间和(所有数非负)”这种单调性显然的题的话应该知道,钦定起点、确定大小这样一个做法在单调意义下可以滑动窗口。在这里前两块其实也是滑窗,因此省掉了内层的二分。时间复杂度$O(n*log_a)$。

当然,把枚举起点的循环放到二分外边会快一点。

也可以改变枚举量(WZQ的做法),就是把二分最小大小 改为 二分前两块的长度,提高答案范围当且仅当第一块的大小$\leq mid$,第二、三块的大小$\geq mid$。这样时间复杂度大概为$O(n*log_n*log_n)$。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define N 100002
 8 inline int read(){
 9     int x=0; bool f=1; char c=getchar();
10     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
11     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
12     if(f) return x;
13     return 0-x;
14 }
15 int n,n1;
16 long long a[N<<1],sfx[N<<1];
17 long long judge(long long x){
18     //printf("x:%d\n",x);
19     int dir,dir2; long long mx=-1;
20     for(int i=1;sfx[i+n1-1]-sfx[i-1]>=x*3;++i){
21         dir=lower_bound(sfx+i,sfx+i+n1,x+sfx[i-1])-sfx;
22         if(sfx[dir]-sfx[i-1]>x) --dir;
23         if(dir<i) continue;
24         dir2=lower_bound(sfx+dir+1,sfx+i+n1,x+sfx[dir])-sfx;
25         if(dir2<=dir) dir2=dir+1;
26         //printf("%d %d %lld %lld %lld\n",dir,dir2,sfx[dir]-sfx[i-1],sfx[dir2]-sfx[dir],sfx[i+n1-1]-sfx[dir2]);
27         //cout<<(dir2<i+n1-1)<<' '<<(sfx[i+n1-1]-sfx[dir2]>=sfx[dir]-sfx[i-1])<<'\n';
28         if(dir2<i+n1-1 && sfx[i+n1-1]-sfx[dir2]>=sfx[dir]-sfx[i-1]) mx=max(mx,sfx[dir]-sfx[i-1]);
29     }
30     //printf("MX:%lld\n",mx);
31     return mx;
32 }
33         
34 int main(){
35     n=n1=read();
36     int i;
37     for(i=1;i<=n;i++) a[i]=a[i+n]=read(), sfx[i]=sfx[i-1]+a[i];
38     n<<=1;
39     for(;i<=n;i++) sfx[i]=sfx[i-1]+a[i];
40     long long l=0,r=(sfx[n]+n-1)/3,mid,ret,ans=-1;
41     while(l<=r){
42         mid=(l+r)>>1;
43         ret=judge(mid);
44         if(ret!=-1) ans=ret, l=mid+1;
45         else r=mid-1;
46     }
47     printf("%lld\n",ans);
48     return 0;
49 }
最外层二分答案(较慢)
 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define N 100002
 8 inline int read(){
 9     int x=0; bool f=1; char c=getchar();
10     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
11     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
12     if(f) return x;
13     return 0-x;
14 }
15 int n,n1;
16 long long a[N<<1],sfx[N<<1];
17 
18 long long judge(int i,long long x){
19     //printf("faq:%d %lld\n",i,x);
20     int dir,dir2;
21     dir=lower_bound(sfx+i,sfx+i+n1,x+sfx[i-1])-sfx;
22     if(sfx[dir]-sfx[i-1]>x) --dir;
23     if(dir<i || dir>=i+n1-2) return -1;
24     
25     dir2=lower_bound(sfx+dir+1,sfx+i+n1,(sfx[dir]<<1)-sfx[i-1])-sfx;
26     if(dir2>=i+n1-1) return -1;
27     
28     //printf("%d %d %lld %lld %lld\n",dir,dir2,sfx[dir]-sfx[i-1],sfx[dir2]-sfx[dir],sfx[i+n1-1]-sfx[dir2]); 
29     if(sfx[i+n1-1]-sfx[dir2]>=sfx[dir]-sfx[i-1]) return sfx[dir]-sfx[i-1];
30     return -1;
31 }
32 int main(){
33     n=n1=read();
34     int i;
35     for(i=1;i<=n;i++) a[i]=a[i+n]=read(), sfx[i]=sfx[i-1]+a[i];
36     n<<=1;
37     for(;i<=n;i++) sfx[i]=sfx[i-1]+a[i];
38     int dir,dir2;
39     long long ans=-1;
40     for(int i=1; i<=n1; i++){
41         long long l=1,r=sfx[n1]/3,mid,ret,res=-1;
42         while(l<=r){
43             mid=(l+r)>>1;
44             ret=judge(i,mid);
45             if(ret!=-1) res=ret, l=mid+1;
46             else r=mid-1;
47         }
48         ans=max(ans,res);
49     }    
50     printf("%lld\n",ans);
51     return 0;
52 }
最外层枚举起点(快一点)
 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 #include<queue>
 8 #define ll long long
 9 using namespace std;
10 const int maxn=1000000+101010;
11 inline int read(){
12     int x=0,f=1;char ch=getchar();
13     for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
14     for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
15     return x*f;
16 }
17 ll n,a[maxn],sum,num[maxn];
18 
19 
20 bool erfe(ll l,ll r,ll he){
21     ll l1=l,r1=r,ans=0;
22     while(r1>=l1){
23         ll mid=r1+l1>>1;    
24         ll qq=num[mid]-num[l-1],ww=num[n]-qq-he;
25         if(qq>=he){
26             if(ww>=he)return 1;
27             else r1=mid-1;    
28         }
29         else l1=mid+1;
30     }
31     return 0;
32 }
33 ll aa;
34 ll erf(ll l,ll r){
35     ll l1=l,r1=r,ans=0;
36     while(r1>=l1){
37         ll mid=r1+l1>>1;
38         if(num[mid]-num[l-1]<=sum){
39             if(erfe(mid+1,r,num[mid]-num[l-1]))ans=max(ans,num[mid]-num[l-1]),l1=mid+1;
40             else r1=mid-1;
41         }
42         else r1=mid-1;
43     }
44     return ans;
45 }
46 
47 ll ans=0;
48 void zj(){
49     for(ll i=1;i<=n;i++){
50         ans=max(ans,erf(i,n+i-1));
51     }
52     printf("%lld",ans);
53     return ;
54 }
55 
56 int main(){
57     n=read();
58     for(ll i=1;i<=n;i++){
59         a[i]=read();
60         num[i]=num[i-1]+a[i];
61         sum+=a[i];
62     }
63     for(ll i=n+1;i<=2*n;i++)a[i]=a[i-n],num[i]=num[i-1]+a[i];
64     sum=sum/3;
65     zj();
66     return 0;
67 }
WZQ的做法

滑窗没写先凑乎吧。

posted @ 2018-10-01 14:45  大本营  阅读(363)  评论(0编辑  收藏  举报