原题链接
首先有一个(O(nk))的很显然的(dp),把荷斯坦牛看成(1),把更赛牛看成(-1),这样就可以很方便地通过前缀和来判断某一段中谁有优势了
考虑怎么优化,观察转移:
[f[i]=min{f[j]+[sum[i]-sum[j]leqslant 0]},1leqslant i-jleqslant k
]
因为([sum[i]-sum[j]leqslant 0])只能为(0)或(1),那么我们开一个双关键字的单调队列维护一下就好了
代码在此:
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <random>
#include <string>
#include <vector>
#include <cmath>
#include <ctime>
#include <queue>
#include <map>
#include <set>
#define IINF 0x3f3f3f3f3f3f3f3fLL
#define u64 unsigned long long
#define pii pair<int, int>
#define mii map<int, int>
#define u32 unsigned int
#define lbd lower_bound
#define ubd upper_bound
#define INF 0x3f3f3f3f
#define vi vector<int>
#define ll long long
#define mp make_pair
#define pb push_back
#define is insert
#define se second
#define fi first
#define ps push
#define $SHOW(x) cout << #x" = " << x << endl
#define $DEBUG() printf("%d %s
", __LINE__, __FUNCTION__)
using namespace std;
#define MAXN 300000
struct Data {
int id, dp, sum;
bool operator < (const Data &rhs) {
return dp == rhs.dp ? sum < rhs.sum : dp < rhs.dp;
}
}q[MAXN + 5];
int n, k, sum[MAXN + 5], f[MAXN + 5], head, tail;
char s[MAXN + 5];
int main() {
scanf("%d%d%s", &n, &k, s + 1);
for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + (s[i] == 'H' ? 1 : -1);
q[tail++] = Data{0, 0, 0};
for (int i = 1; i <= n; ++i) {
while (head < tail && q[head].id < i - k) head++;
Data cur{i, f[i] = q[head].dp + (sum[i] - q[head].sum <= 0), sum[i]};
while (head < tail && cur < q[tail - 1]) tail--;
q[tail++] = cur;
}
printf("%d
", f[n]);
return 0;
}