~~NOIP的蓝题果然还是好难啊啊啊啊~~
前言:
作为一道 NOIP 的真题, 这道题放在 T2 难度并不是特别大,不过考点还是比较偏的,扩展KMP和树状数组的组合,并且还带有一定的思维难度,估计是当年不少人低分的题目,同时也给了一些人翻盘的机会。总而言之, 是道好题。
Description:
将字符串 $s$ 划分成 $(AB)^iC$的形式,并且满足 $A$ 中出现奇数次的字符数小于等于 $C$ 中出现奇数次的字符数。
Solution:
首先发现 $(AB)^i$ 中的 $AB$ 是一个循环节,因此想到枚举循环节的长度。
由于 $A,B,C$ 的长度均大于等于1,所以循环节长度应为 $2 \leq len \leq n-1$。
画个图发现, 对于长度为 $i$ 的前缀, 它最多循环的次数为 $z_{i+1}/i$ 下取整 $+1$。设循环次数为 $k$, $f(i,j)$表示 $s[i:j]$ 中出现奇数次的字符数。 当 $k$ 为奇数时, $F(C) = f(i+1, n)$, 因此只要找到满足 $f(1, j) \leq f(i+1, n)$ 的 $j$ 的个数即可, 然后用乘法原理统计。当 $k$ 为偶数时, $F(A)$ 必然等于 $0$, $F(C) = f(1, n)$。
实现可以采用数据结构维护,本题最好用树状数组。
时间复杂度 $O(n log 27)$。
CODE
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') f = -f; c = getchar();} while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();} return x * f; } const int N = (1 << 20) + 10, C = 26; int n; char s[N]; int z[N]; inline void getZ() { z[1] = n; for (int i = 2, l = 0, r = 0; i <= n; ++ i) { if (i <= r) z[i] = min(z[i-l+1], r-i+1); while (i + z[i] <= n && s[i+z[i]] == s[z[i] + 1]) ++ z[i]; if (i + z[i] - 1 > r) r = i + z[i] - 1, l = i; } } int tot[C+10], cnt[C+10], c[C+10]; inline int lowbit (int x) {return x & -x;} inline void add(int x, int v) {for (; x <= C+1; x += lowbit(x)) c[x] += v;} inline int ask (int x) {int res = 0; for (; x > 0; x -= lowbit(x)) res += c[x]; return res;} int ans, all, lst, now; signed main () { int T = read(); while (T --) { scanf("%s", s + 1); n = strlen(s + 1); for (int i = 1; i <= n; ++ i) z[i] = 0; getZ(); for (int i = 1; i <= n; ++ i) if (i + z[i] == n + 1) z[i] --; for (int i = 1; i <= C+1; ++ i) tot[i] = cnt[i] = c[i] = 0; ans = all = lst = now = 0; for (int i = 1; i <= n; ++ i) tot[s[i]-'a'+1] ++; for (int i = 1; i <= C; ++ i) all += (tot[i] & 1); lst = all; for (int i = 1; i <= n; ++ i) { if (tot[s[i]-'a'+1] & 1) lst --; else lst ++; if (cnt[s[i]-'a'+1] & 1) now --; else now ++; tot[s[i]-'a'+1] --; cnt[s[i]-'a'+1] ++; if (i > 1 && i < n) { int t = z[i+1] / i + 1; ans += (t/2) * ask(all+1) + (t - t/2) * ask(lst+1); } add(now+1, 1); } printf("%lld\n", ans); } return 0; }View Code
标签:10,return,P7114,int,leq,while,解题,ans,NOIP2020 From: https://www.cnblogs.com/CikL1099/p/17036884.html