直接dp的话,复杂度是n*k的。
但是我们可以发现,如果我们给每次选择,一个负的代价,那么整体的最优解是一个凸包。我们就可以wqs二分了。
但是要注意,因为选择的次数是小于等于k,但是最优解一定是选k次。因为我们每次转移是从 i - l 转移的,所以如果 i < l 我们也需要转移。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
#define int long long
using namespace std;
const int N=1e6+10;
int n,k,l,a[N],sum[N],dp[N],f[N]; char str[N];
inline int check(int mid){
for(int i=1;i<=n;i++){
dp[i]=dp[i-1],f[i]=f[i-1];
if(i>=l&&dp[i]<dp[i-l]+sum[i]-sum[i-l]-mid)
dp[i]=dp[i-l]+sum[i]-sum[i-l]-mid,f[i]=f[i-l]+1;
else if(i<l&&dp[i]<sum[i]-mid) dp[i]=sum[i]-mid,f[i]=1;
}
return f[n];
}
inline int calc(int flag){
for(int i=1;i<=n;i++) a[i]=flag^(str[i]>='A'&&str[i]<='Z');
int l=0,r=n,res=0,ans;
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]; res+=sum[n];
while(l<=r){
int mid=l+r>>1;
if(check(mid)<=k) ans=mid,r=mid-1;
else l=mid+1;
}
check(ans);
return res-dp[n]-k*ans;
}
signed main(){
cin>>n>>k>>l; scanf("%s",str+1);
cout<<min(calc(1),calc(0));
return 0;
}