【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 }
滑窗没写先凑乎吧。
嗯。。。