Description
有 n×k 个人排成一行,从左往右按 0,1,…nk−1 编号。每个人初始都面对着一个方向 L 或 R。给出一个字符串 s0…n−1,则第 i 个人的方向为 simodn。
接下来进行若干轮操作,每一轮所有人同时进行如下操作:
- 若当前某人面对左边,且他左边的人中面对右边的人数超过一半,则他转头面向右边。
- 若当前某人面对右边,且他右边的人中面对左边的人数超过一半,则他转头面向左边。
操作进行 10100 轮,请求出所有轮中每个人转头的次数之和。
n,k≤2×105。
Solution
考虑 k=1 怎么做。
先将 L 看成 −1,R 看成 1,并求前缀和。设 si 表示 [1,i] 的前缀和。
这里钦定 sn≥0,如果不满足则可以全局取反并翻转。
那么操作分两种:
- i 向左且 si≥0。
- i 向右且 sn−si<0。
把折线图画出来(图源 atcoder):

经过手玩可以发现图中的所有红蓝边都会翻转,因为把所有在 y=sn 上方的折线拿出来,在 y=sn+1 及以上的部分显然都会翻转,而连续段两端分别是 R 和 L,都分别满足条件。
同时所有在 y=0 和 y=sn 之间的黄色折线中的向左段也会翻转。
由于在 y=sn 上方的折线都会翻转到下面,所以 sn 在一次操作后一定会变成全局最大值。
所以进行一次操作后所有向右的线段都不会再翻转,只有部分向左的线段可能翻转。
对于一个向左的线段 i,其会翻转的条件显然为 s1,s2,…si 中存在一个正数。这个可以归纳证明:找到第一个正数位置 x,显然 x 之前都不会翻转,而 x 一定会变为向右,对于后面的第一个向左的位置 y,由于 [x+1,y−1] 都是向右,所以 sy 也会是正数,所以 y 最终也会向右。每次找到后面的第一个向左的位置就一定能操作完。
注意到 sn=0 时找不到第一个 x,所以需要特判。
对于 sn>0 的情况,一个位置被操作的条件为:
- i 初始向左且 si≥0,操作 1 次。
- i 初始向右且 j=1maxisj≥0,操作 2 次。
k>1 时解个不等式即可。
时间复杂度:O(n)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <bits/stdc++.h>
#define int int64_t
const int kMaxN = 2e5 + 5, kMod = 998244353;
int n, k; int s[kMaxN], f[kMaxN]; std::string str;
int cei(int x, int y) { if (!y) return x <= 0 ? 0 : k; if (y < 0) x = -x, y = -y; if (x >= 0) return (x + y - 1) / y; else return 0; }
void dickdreamer() { std::cin >> n >> k >> str; if (std::count(str.begin(), str.end(), 'R') <= (n - 1) / 2) { for (auto &c : str) c = 'L' + 'R' - c; std::reverse(str.begin(), str.end()); } str = " " + str; for (int i = 1; i <= n; ++i) { s[i] = s[i - 1] + (str[i] == 'L' ? -1 : 1); } if (!s[n]) { int ans = 0; for (int i = 1; i <= n; ++i) { if (str[i] == 'L' && s[i] >= 0 || str[i] == 'R' && s[i] > 0) ++ans; } std::cout << 1ll * ans * k % kMod << '\n'; return; } f[0] = (*std::max_element(s + 1, s + 1 + n) > 0 ? 1 : k); for (int i = 1, mx = 0; i <= n; ++i) { mx = std::max(mx, s[i]); if (mx > 0) f[i] = 0; else f[i] = f[i - 1]; } int ans = 0; for (int i = 1; i <= n; ++i) { if (str[i] == 'L') { if (f[i] < k) ans += k - f[i]; } else { int lim = s[n] * k; int cnt = cei(lim + 1 - s[i], s[n]); if (cnt <= k - 1) ans += 2 * (k - cnt); } } std::cout << ans % kMod << '\n'; }
int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; while (T--) dickdreamer(); return 0; }
|