zoukankan      html  css  js  c++  java
  • 【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
    ",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
    ",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])<<'
    ';
    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
    ",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
    ",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
    ",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
    ",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
    ",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的做法

    滑窗没写先凑乎吧。

  • 相关阅读:
    Mysql
    JavaScript常用事件
    css
    HTML
    判断pc还是手机打开跳转到别的网页
    queue 队列
    兼容firstChild和firstElementChild
    总结各种width,height,top,left
    原生js提取非行间样式
    ie8 不支持media
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/9734669.html
Copyright © 2011-2022 走看看