题解:AT_abc300_f [ABC300F] More Holidays

· · 题解

题目简述

给你一个长度为 N 并且由 ox 组成的字符串 S,现将 S 复制 M 遍,并且要恰好将其中的 Kx 改为 o,求子串中之包括 o 的最大长度。

主要思路

首先可以发现,答案所代表的子串可以由一下三部分组成:(由于答案里面全是 o,所以下面指的是将答案修改前的样子)。

  1. 设第一部分的长度为 x0 \le x < N),则第一部分为 S_{N-x+1} \sim S_{N}
  2. 设第三部分的长度为 x0 \le x < N),则第三部分为 S_{1} \sim S_{x}

假设 Sx 的数量为 y,那么为了使答案最大,第二部分字符串 S 的数量是 \min(\lfloor \frac{K}{y} \rfloor,M-2)(减 2 表示减掉第一部分和第三部分),但还有一种情况,第二部分字符串的数量是 \min(\lfloor \frac{K}{y} \rfloor - 1,M-2),将多出来的 N 次修改机会放到第一部分和第三部分。接着我们对于这两种情况将 K 减去第二部分字符串 S 的数量乘 y,就是第一部分和第三部分的修改次数。

再不难发现,对于复制了 M 次的(M>1)字符串第一部分和第三部分拼起来是连续的,此时我们可以使用双指针算法求出修改后全为 o 的子串的最大长度:

当 $S_{l} \sim S_{r}$ 中 `x` 的数量大于 $K$ 时,就不断右移左指针,虽然题目中要求恰好改变 $K$ 个字符,但实际上移动后的 $S_{l} \sim S_{r}$ 中 `x` 的数量只要小于等于 $K$,$r-l+1$ 就可以与当前答案取最大值了,因为多出来的可以放到剩下任意位置。 需要注意的是:如果 $K > y$,那么答案字符串一定由那三部分组成,但反之则不一定,随便在 $S'$ 中截取一段即可。还有:当 $M=1$ 时 $K$ 一定小于等于 $y$,并且无需将 $S$ 复制一遍。 ## AC Code ```cpp #include<cmath> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #ifdef ONLINE_JUDGE #define getchar getchar_unlocked #endif template<typename T> void read(T &x) { int f = 1; x = 0; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-')f = -1; ch = getchar(); }while (isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }x *= f; } template<typename T, typename ...Args> void read(T &x, Args &...args) { read(x); read(args...); } template<typename T> void print(T x) { if (x < 0) { putchar('-'); x = -x; }if (x > 9) { print(x / 10); }putchar(char(x % 10 + 48)); } template<typename T, typename ...Args> void print(T x, Args ...args) { print(x); putchar(' '); print(args...); } const int INT_INF = 0x3f3f3f3f; const long long LL_INF = 0x3f3f3f3f3f3f3f3f; template<typename T1, typename T2> T1 _pow(T1 a, T2 b) { T1 x = 1, y = a; while(b > 0) {if (b & 1) x *= y; y *= y; b >>= 1; } return x; } // ---------------------------- // ---------------------------- // ---------------------------- int main() { long long n, m, k; read(n, m, k); string s; cin >> s; // ---------------------------- int l = 0; long long cnt = 0, maxn = 0, ans = 0; for (int i = 0; i < n; i++) cnt += (s[i] == 'x'); if (cnt * m <= k) { print(n * m); return 0; } if (m > 1) s += s; if (k <= cnt) { long long t = 0; // 表示 s[l]~s[r] 中 x 的数量 for (int r = 0; r < (int)s.length(); r++) { t += (s[r] == 'x'); while (l < r && t > k) t -= (s[l++] == 'x'); if (t <= k) maxn = max(maxn, r - l + 1 - t); } ans = max(ans, maxn + k); } else { long long x = min(k / cnt * n, (m - 2) * n); l = 0; maxn = 0; // x 表示第二部分的长度 long long w = k - min(k / cnt * cnt, (m - 2) * cnt); // 表示第一部分和第三部分的可修改次数 long long t = cnt - (s[n - 1] == 'x'); // 表示 s[l]~s[r] 中 x 的数量 for (int r = n - 1; r < n * 2; r++) { t += (s[r] == 'x'); while (l < r && l < n && t > w) t -= (s[l++] == 'x'); if (t <= w) maxn = max(maxn, r - l + 1ll); } ans = max(ans, x + maxn); x = min((k / cnt - 1) * n, (m - 2) * n); l = 0; maxn = 0; w = k - min((k / cnt - 1) * cnt, (m - 2) * cnt); t = cnt - (s[n - 1] == 'x'); for (int r = n - 1; r < n * 2; r++) { t += (s[r] == 'x'); while (l < r && l < n && t > w) t -= (s[l++] == 'x'); if (t <= w) maxn = max(maxn, r - l + 1ll); } ans = max(ans, x + maxn); } // ---------------------------- print(ans); return 0; } ```