題解「JOI 2014 Final 年輪蛋糕」
阿新 • • 發佈:2020-10-14
二分套路題,想到可以二分最小塊大小,但是 \(\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; }