题解 星际广播

传送门

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;
}
posted @ 2022-04-07 21:12  Administrator-09  阅读(2)  评论(0编辑  收藏  举报