题解「JOI 2014 Final 年轮蛋糕」
二分套路题,想到可以二分最小块大小,但是 \(\text{check}\) 函数是个问题。
这里给出两种 \(\text{check}\) 的实现,时间复杂度分别为 \(O(n\log n)\) 和 \(O(n)\)。
\(O(n \log n)\) 的暴力做法:枚举每个 \(i\),\(\text{lower_bound}\) 一个 \(j\),再根据 \(j\) 来 \(\text{lower_bound}\) 一个 \(k\),满足 \(\sum\limits_{i\leq x\leq j} a_x\geq mid,\sum\limits_{j< x\leq k}a_x\geq mid\)。
\(O(n)\) 的做法:枚举每个 \(i\),再用两个指针 \(p,q\) 维护满足 \(\sum\limits_{p\leq x\leq i} a_x\geq mid\) 和 \(\sum\limits_{i<x\leq q}a_x\geq mid\),其中 \(p\) 为满足条件的最大的下标,\(q\) 为满足条件的最小的下标,这两个量都有单调性,所以使用 \(\text{two-pointer}\) 维护即可。
那么就可以在 \(O(n\log \sum a_i)\) 或 \(O(n \log^2 \sum a_i)\) 的时间复杂度内解决该问题。
#include<cstdio>
typedef long long ll;
ll n,sum; int a[100005];
inline int read() {
register int x=0,f=1;register char s=getchar();
while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
return x*f;
}
inline bool check(ll val) {
int p=0,q=0; ll sum1=0,sum2=0;
for(register int i=0;i<n;++i) {
sum1+=a[i];sum2-=a[i];
while(p<=i&&sum1-a[p]>=val) {sum1-=a[p];++p;}
while(q<=n-1&&sum2<val) {sum2+=a[q];++q;}
if(sum2<val) return 0;
if(sum1>=val&&sum-sum1-sum2>=val) return 1;
}
return 0;
}
int main() {
n=read();
for(register int i=0;i<n;++i) sum+=(a[i]=read());
ll l=0,r=sum,res=0;
while(l<=r) {
ll mid=l+r>>1;
if(check(mid)) l=mid+1,res=mid;
else r=mid-1;
}
printf("%lld\n",res);
return 0;
}

浙公网安备 33010602011771号