AT_arc168_e [ARC168E] Subsegments with Large Sums 题解
AT_arc168_e [ARC168E] Subsegments with Large Sums
给定长度为 \(n\) 的数列 \(\{a_i\}\) 和两个参数 \(k, s\),将 \(\{a_i\}\) 划分为 \(k\) 段,最大化和 \(\geq s\) 的段数。
\(k \leq n \leq 2.5e5\)。
思路
有划分为恰好 \(k\) 段的限制,我们可以考虑wqs二分。发现直接将答案作为 \(y\) 轴是没有单调性的,因此只能换一下。
发现由于我们要让有贡献的段数尽量大,因此要让没有贡献的段长尽量小,因此没有贡献的段长直接设为1。
然后就又发现我们要让有贡献的段数尽量大,也要让有贡献的段长尽量小。
因此我们设 \(f(x)\) 为总贡献为 \(x\) 时有贡献的段的总长度。然后发现当可以划分的合法序列的段数大于 \(k\) 的时候,一定可以通过某种合并方式使得这个序列的划分的段数变为 \(k\) 同时仍然合法。
因此对于一个序列,其合法的限制条件就是 \(k\le x+n-f(x)\),即 \(\sum{r-l}\le n-k\)。
然后就将 \(sum{r-l}\) 作为 \(y\) 轴,二分的总贡献 \(x\) 作为 \(x\) 轴,直接wqs二分即可。
code
需要注意同样需要特殊判定多个点在同一条直线上的情况。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+7;
int n,K,s,a[N],sum[N],pre[N];
pair <int,int> f[N];
bool check(int k){
for(int i=1;i<=n;i++){
f[i]=f[i-1];if(pre[i]>=1) f[i]=min(f[i],{f[pre[i]-1].first+i-pre[i]-k,f[pre[i]-1].second+1});
}
return (f[n].first+f[n].second*k<=n-K);
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>K>>s;
for(int i=1;i<=n;i++) cin>>a[i],sum[i]=sum[i-1]+a[i];
for(int i=1,loc=0;i<=n;i++) {while(sum[i]-sum[loc-1]>=s) loc++;pre[i]=loc-1;}
int l=1,r=n,ansk=l;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) ansk=mid,l=mid+1;
else r=mid-1;
}
int eee=check(ansk),x=min(f[n].second,K),y=f[n].first+f[n].second*ansk;
while(y+ansk<=n-K&&x<K) x++,y+=ansk;
cout<<x;return 0;
}

浙公网安备 33010602011771号