[BZOJ2084][Poi2010]Antisymmetry
[BZOJ2084][Poi2010]Antisymmetry
试题描述
对于一个 01
字符串,如果将这个字符串 0
和 1
取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如 00001111
和 010101
就是反对称的,1001
就不是。
现在给出一个长度为 \(N\) 的 01
字符串,求它有多少个子串是反对称的。
输入
第一行一个正整数 \(N(N \le 500,000)\)。第二行一个长度为N的01字符串。
输出
一个正整数,表示反对称子串的个数。
输入示例
8
11001011
输出示例
7
数据规模及约定
见“输入”
题解
将原串异或上 101010...
然后直接上 manacher。
我们不难证明“一个01串反对称”等价于“一个01串异或上 101010...
后是回文串”等价(提示:这样的串必须是偶数位,那么从两边往中间每一位一定异或的是不同的数)。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 500010
int n, alp[maxn<<1], len[maxn<<1];
char str[maxn], S[maxn<<1];
int main() {
n = read();
scanf("%s", str + 1);
rep(i, 1, n) S[(i<<1)-1] = str[i] - '0' ^ (i & 1), S[i<<1] = '#';
n <<= 1;
rep(i, 1, n) alp[i] = alp[i-1] + (0 <= S[i] && S[i] <= 1);
len[1] = 1;
int mxi = 1;
rep(i, 2, n) {
len[i] = 1;
if(mxi + len[mxi] - 1 >= i) len[i] = min(max(1, len[(mxi<<1)-i]), mxi + len[mxi] - i);
while(i - len[i] > 0 && i + len[i] <= n && S[i-len[i]] == S[i+len[i]]) len[i]++;
if(mxi + len[mxi] < i + len[i]) mxi = i;
}
int ans = 0;
rep(i, 1, n) if(S[i] == '#')
ans += alp[i] - alp[i-len[i]];
printf("%d\n", ans);
return 0;
}