题解 星际广播
wqs 二分的模板题
令 \(g(i)\) 为选 \(i\) 个物品的最大收益,考虑 \((i, g(i))\) 构成的凸包
若用斜率为 \(k\) 的直线切这个凸包,令切点横坐标为 \(x\)
那么切线截距为 \(g(x)-kx\)
那么将每次选择物品的收益减少 \(k\),求最大收益,加回去就可以求出 \(g(m)\) 了
- 关于 wqs 二分的斜率取值问题:
若可选的物品个数每次加 1 且价值函数的值域都是整数,则用整数斜率可以切到所有点
原因是凸包上相邻两点间横坐标差值为 1 且纵坐标差为整数
复杂度 \(O(n\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define ll long long
//#define int long long
int n, m, l;
char s[N];
namespace force{
int pre[N], f[5010][5010], ans=INF;
int solve(char c) {
for (int i=1; i<=n; ++i) pre[i]=pre[i-1]+(s[i]!=c);
for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) f[i][j]=0;
f[0][0]=0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j)
f[i][j]=max(f[i-1][j], f[i-l][j-1]+pre[i]-pre[i-l]);
int ans=0;
for (int i=1; i<=m; ++i) ans=max(ans, f[n][i]);
return pre[n]-ans;
}
void solve() {
// cout<<double(sizeof(f))/1000/1000<<endl;
if (1ll*m*l>=n) {puts("0"); return ;}
ans=min(ans, solve('R'));
ans=min(ans, solve('B'));
ans=min(ans, solve('Y'));
cout<<ans<<endl;
}
}
namespace task1{
random_device seed;
mt19937 rnd(seed());
int pre[N], ans=INF;
int **f;
int solve(char c) {
for (int i=1; i<=n; ++i) pre[i]=pre[i-1]+(s[i]!=c);
for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) f[i][j]=0;
f[0][0]=0;
for (int i=l; i<=n; ++i)
for (int j=1; j<=m; ++j)
f[i][j]=max(f[i-1][j], f[i-l][j-1]+pre[i]-pre[i-l]);
int ans=0;
for (int i=1; i<=m; ++i) ans=max(ans, f[n][i]);
return pre[n]-ans;
}
void test(char c) {
for (int i=1; i<=n; ++i) pre[i]=pre[i-1]+(s[i]!=c);
ans=min(ans, n-pre[l*m]);
ans=min(ans, n-(pre[n]-pre[n-l*m]));
}
void solve() {
// cout<<double(sizeof(f))/1000/1000<<endl;
if (1ll*m*l>=n) {puts("0"); return ;}
test('R'); test('B'); test('Y');
if (1ll*n*m>5e8) {cout<<ans<<endl; return ;}
f=new int*[n+5];
for (int i=0; i<=n; ++i) f[i]=new int[m+2];
ans=min(ans, solve('R'));
if (clock()>500000) {cout<<ans<<endl; return ;}
ans=min(ans, solve('B'));
if (clock()>600000) {cout<<ans<<endl; return ;}
ans=min(ans, solve('Y'));
cout<<ans<<endl;
}
}
namespace task{
int pre[N], ans=INF;
pair<int, int> f[N];
void check(int mid) {
// cout<<"check: "<<mid<<endl;
for (int i=l; i<=n; ++i) {
if (f[i-l].sec+pre[i]-pre[i-l]-mid > f[i-1].sec) f[i]={f[i-l].fir+1, f[i-l].sec+pre[i]-pre[i-l]-mid};
else f[i]=f[i-1];
}
// cout<<"res: ("<<f[n].fir<<','<<f[n].sec<<")"<<endl;
}
void solve(char c) {
for (int i=1; i<=n; ++i) pre[i]=pre[i-1]+(s[i]!=c);
int l=0, r=n, mid;
while (l<=r) {
mid=(l+r)>>1;
check(mid);
if (f[n].fir<=m) r=mid-1;
else l=mid+1;
}
mid=r+1;
// cout<<"mid: "<<mid<<endl;
check(mid);
// cout<<f[n].sec<<endl;
ans=min(ans, pre[n]-(f[n].sec+m*mid));
}
void solve() {
if (n<=m*l) {puts("0"); return ;}
solve('R'); solve('B'); solve('Y');
cout<<ans<<endl;
}
}
signed main()
{
freopen("radio.in", "r", stdin);
freopen("radio.out", "w", stdout);
scanf("%d%d%d%s", &n, &m, &l, s+1);
// if (n<=5000) force::solve();
// else task1::solve();
task::solve();
return 0;
}