NOIP2020 T2
题意:把一个字符串切成形如 \(ABAB......ABC\) 的形式,其中 \(A,B,C\) 都是非空子串且 \(F(A) \le F(C)\)(\(F(S)\) 表示 \(S\) 中出现奇数次的数的个数)。问方法数。(字符串长度 \(|S| \le 2^{20}\))
思路:
1.看到一道题可以首先想暴力:按照题意模拟就行了,先枚举 \(A\) 的长度,再枚举 \(B\) 的长度,最后枚举 \(AB\) 的重复次数,剩下的就是 \(C\),然后判断行不行,(就是判断 \(AB\) 能否重复这么多次)\(~~~~~~\)(至少 \(\Theta(n^4)\))
小优化1.判断 \(AB\) 行不行时,用哈希,少个 \(n\)
小优化2.很明显, \(A\) 和 \(C\) 肯定是前后缀,所以可以 \(\Theta(n)\) 预处理掉!
小优化3.又枚举 \(A\) 又枚举 \(B\) 太浪费了。我们想到一般这种可以少枚举一个再用数据结构,所以枚举 \(AB\) 的总长度,对于一种现在枚举的情况,现在我们要求的就是从1到 \(i - 1\) 中取 \(A\),使 \(F(A) \le F(C)\) 的个数。很明显用个树状数组搞定。
小优化4.我们发现,当枚举重复次数时,如果在原有次数上增添一个 “\(ABAB\)", 字符串中数出现的奇偶性不变,所以 \(A\) 的取值的可行性也不会改变(即满足 \(F(A) \le F(C)\) 的个数是定值)。所以我们不需要每次都用树状数组求,这样就带两个 \(log\) 了。只需要算出重复1和2次的情况,后面 \(\Theta(1)\) 计算贡献即可。
用这种方法,这题好像就完了?
上代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int N = 2e6 + 10, base = 13331, mod = 998244353;
int T, n;
char s[N];
ll ans;
int c[30], now, pre[N], suf[N];
ull pw[N], hx[N];
int tr[30];
inline int lb(int x)
{
return x & -x;
}
inline void add(int x)
{
++x;
while(x <= 27)
{
++tr[x];
x += lb(x);
}
}
inline int qry(int x)
{
int res = 0;
++x;
while(x)
{
res += tr[x];
x -= lb(x);
}
return res;
}
inline ull gethx(int l, int r)
{
return hx[r] - hx[l - 1] * pw[r - l + 1];
}
int w[2];
int main()
{
scanf("%d", &T);
pw[0] = 1;
for(int i = 1; i <= 2e6; ++i) pw[i] = pw[i - 1] * base;
while(T--)
{
scanf("%s", s + 1); n = strlen(s + 1);
memset(c, 0, sizeof c);
now = 0;
for(int i = 1; i <= n; ++i)
{
hx[i] = hx[i - 1] * base + s[i]; //哈希判断字符串相等
c[s[i] - 'a'] ^= 1;
if(c[s[i] - 'a']) ++now;
else --now;
pre[i] = now; //预处理前后缀权值 (A/C)
}
memset(c, 0, sizeof c);
now = 0;
for(int i = n; i; --i)
{
c[s[i] - 'a'] ^= 1;
if(c[s[i] - 'a']) ++now;
else --now;
suf[i] = now;
}
memset(tr, 0, sizeof tr);
ans = 0;
for(int i = 2; i < n; ++i)
{
add(pre[i - 1]); //枚举AB长度,AB中有多少个A符合用树状数组算
w[1] = qry(suf[i + 1]); //奇偶分别算,后面奇偶性相同O(1)算答案
if((i << 1) < n)
w[0] = qry(suf[(i << 1) + 1]);
for(int j = 1; i * j < n; ++j)
if(hx[i] == gethx(i * (j - 1) + 1, i * j))
ans += w[j & 1];
else break; //枚举AB最多重复次数
}
cout << ans << '\n';
}
return 0;
}

浙公网安备 33010602011771号