[BZOJ3160]万径人踪灭(FFT + Manacher)

Address

洛谷 P4199
BZOJ 3160

Solution

  • 先不考虑选出的字符是否为连续的一段
  • 考虑枚举对称轴 i i (如果对称轴在原串第 x x 个字符则对称轴为 2 x 2x ,如果在第 x x 和第 x + 1 x+1 个字符之间则为 2 x + 1 2x+1
  • 如果有 k k 字符关于对称轴 i i 对称,则可以为答案贡献 2 k 2^k (实际上可能不是 2 k 2^k ,注意空串、恰好在对称轴上的字符等细节)
  • 考虑求 k i k_i 表示以 i i 为对称轴,满足 j + k = i j+k=i 且原串第 j j 和第 k k 个字符相同的有序二元组 ( j , k ) (j,k) 个数
  • 把字符 a a 和字符 b b 分开处理
  • 定义序列 A A 的第 i i 个元素表示原串第 i i 个字符是否为 a a ,是则为 1 1 ,否则为 0 0
  • 序列 B B 的第 i i 个元素表示原串第 i i 个字符是否为 b b ,是则为 1 1 ,否则为 0 0
  • 那么
  • k i = j + k = i ( [ A [ j ] = 1 , A [ k ] = 1 ] + [ B [ j ] = 1 , B [ k ] = 1 ] ) k_i=\sum_{j+k=i}([A[j]=1,A[k]=1]+[B[j]=1,B[k]=1])
  • = j = 0 i ( A [ j ] × A [ i j ] + B [ j ] × B [ i j ] ) =\sum_{j=0}^i(A[j]\times A[i-j]+B[j]\times B[i-j])
  • 是一个卷积的形式
  • 直接上 FFT , O ( n log n ) O(n\log n)
  • 然后去掉字符为连续一段的方案数
  • 将原串用无关字符隔开之后, Manacher 求回文半径,设为 r [ ] r[]
  • 最后统计答案时,对于一个 i [ 2 , 2 n ] i\in[2,2n]
  • 如果 i i 为奇数(对称轴在两个字符之间)时为答案贡献
  • 2 k i 2 r i 2 2^{\lfloor\frac{k_i}2\rfloor}-\lfloor\frac{r_i}2\rfloor
  • 否则 i i 为偶数(对称轴为一个字符),为答案贡献
  • 2 k i 2 + 1 r i 2 1 2^{\lfloor\frac{k_i}2\rfloor+1}-\lfloor\frac{r_i}2\rfloor-1
  • 复杂度 O ( n log n ) O(n\log n)

Code

#include <bits/stdc++.h>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Pow(k, n) for (k = 1; k < n; k <<= 1)
#define Step(i, a, b, x) for (i = a; i <= b; i += x)

typedef std::complex<double> cplx;

template <class T>
T Min(T a, T b) {return a < b ? a : b;}

template <class T>
void Swap(T &a, T &b) {T t = a; a = b; b = t;}

const int N = 1e5 + 5, M = N << 1, L = 3e5 + 5, ZZQ = 1e9 + 7;
const double pi = acos(-1.0);

int n, m, r[M], rev[L], ff = 1, tot = 0, ans, p2[N], ansa[M], ansb[M];
char s[N], t[M];
cplx a[L], b[L];

void manacher()
{
	int i, mx = 0, pos = 0;
	For (i, 1, m)
	{
		r[i] = mx > i ? Min(r[(pos << 1) - i], mx - i) : 1;
		while (t[i - r[i]] == t[i + r[i]]) r[i]++;
		if (i + r[i] > mx) mx = i + r[i], pos = i;
	}
}

void FFT(int n, cplx *a, int op)
{
	int i, j, k;
	For (i, 0, n - 1) if (i < rev[i]) Swap(a[i], a[rev[i]]);
	Pow(k, n)
	{
		cplx x(cos(pi / k), sin(pi / k) * op);
		Step (i, 0, n - 1, k << 1)
		{
			cplx w(1, 0);
			For (j, 0, k - 1)
			{
				cplx u = a[i + j], v = w * a[i + j + k];
				a[i + j] = u + v;
				a[i + j + k] = u - v;
				w *= x;
			}
		}
	}
}

int main()
{
	int i;
	scanf("%s", s + 1);
	n = strlen(s + 1);
	p2[0] = 1;
	For (i, 1, n) p2[i] = 2 * p2[i - 1] % ZZQ;
	t[0] = '%'; t[m = 1] = '#';
	For (i, 1, n) t[++m] = s[i], t[++m] = '#';
	t[m + 1] = '^';
	For (i, 1, n)
		if (s[i] == 'a') a[i] = 1, b[i] = 0;
		else a[i] = 0, b[i] = 1;
	while (ff <= (n << 1)) ff <<= 1, tot++;
	manacher();
	For (i, 0, ff - 1)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
	FFT(ff, a, 1); FFT(ff, b, 1);
	For (i, 0, ff - 1) a[i] *= a[i], b[i] *= b[i];
	FFT(ff, a, -1); FFT(ff, b, -1);
	For (i, 0, ff - 1) a[i] /= ff, b[i] /= ff;
	For (i, 2, m - 1) ansa[i] = a[i].real() + 0.5,
		ansb[i] = b[i].real() + 0.5;
	For (i, 2, m - 1)
		if (i & 1) ans = (ans + (p2[(ansa[i] >> 1) + (ansb[i] >> 1)] - 1
			- (r[i] >> 1) + ZZQ) % ZZQ) % ZZQ;
		else ans = (((ans + p2[(ansa[i] >> 1)
			+ (ansb[i] >> 1)]) % ZZQ - (r[i] >> 1) + ZZQ) % ZZQ
			+ p2[(ansa[i] >> 1) + (ansb[i] >> 1)] - 1) % ZZQ;
	std::cout << ans << std::endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/83903369
今日推荐