1 条题解
-
0
Part 1
首先,通过模拟,不难发现:开奖过程实际上是在环形字符串 中截取子串,并取第 大字母(设 )。
不难发现,最难处理的就是「第 大」。于是,我们需要一条重要性质:
在一个含有不少于 个数的序列中,任意一个数 成为第 大数的充分必要条件是:
设序列中大于 的数共 个,大于等于 的数共 个,则有 。
这个性质很显然,证明不给了。
Part 2
注意到 ( 中最多有 种字符),我们将每个字符分开处理。
先将 复制一遍接到末尾。
对于某个字符 ,设 为 ( 的前 个字符)中大于 的字符个数, 为 ( 的前 个字符)中大于等于 的字符个数。
显然,它们可以用前缀和处理,并且单调不降。
然后,枚举每个 ,依据上面性质查找符合要求的最小 与最大 ,合法的 即为两者之间的数,可以 统计答案。注意 。
可以发现,如果升序枚举 ,最小 与最大 单调不降,于是每次都可以从上次的位置接着升序枚举。
因此,每个字符可以做到 求解。总时间复杂度 ,空间复杂度 。
Part 3
std:
#include <iostream> using namespace std; const int PP = 2000005; int l[PP], le[PP], pp[PP]; // s[i] t[i] P int p, k; int lastf1 = 0, lastf2 = 0; // 上次查询的记录 int f1(int lf, int rg, int v) { // 最大 r:s <= k - 1 if (l[lf] > v) return lf - 1; // 没有符合要求的 r while (l[lastf1 + 1] <= v && lastf1 < rg) lastf1 ++; return lastf1; } int f2(int lf, int rg, int v) { // 最小 r:t >= k if (le[rg] < v) return rg + 1; // 没有符合要求的 r while (le[lastf2] < v && lastf2 < rg) lastf2 ++; return lastf2; } int main() { cin >> p >> k; for (int i = 1; i <= p; i ++) { char c; cin >> c; pp[i] = pp[i + p] = c - 64; // 读入 + 复制 + A,B,C,...,Z 转为 1,2,3,...,26 } for (int cases = 1; cases <= 26; cases ++) { lastf1 = lastf2 = 0; for (int i = 1; i <= p * 2; i ++) { l[i] = l[i - 1]; le[i] = le[i - 1]; if (pp[i] < cases) l[i] ++; if (pp[i] <= cases) le[i] ++; } long long ans = 0; for (int i = 1; i <= p; i ++) ans += max(f1(i + k - 1, i + p - 1, k + l[i - 1] - 1) - f2(i + k - 1, i + p - 1, k + le[i - 1]) + 1, 0); cout << ans << endl; } return 0; }
- 1
信息
- ID
- 195
- 时间
- 2000ms
- 内存
- 128MiB
- 难度
- 7
- 标签
- 递交数
- 3
- 已通过
- 2
- 上传者