天天看點

BZOJ 3160 萬徑人蹤滅

題面

題目大意

略.

題解

FFT跑一遍, 由于不能連續, 是以再跑一次manacher減去不符合題意的部分.

這道題展現了FFT的一種用途: 在序列中元素個數不多的情況下, 找關于某個中心對稱的相同字元.

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>

const int N = (int)1e5, MOD = (int)1e9 + 7;

namespace convolution
{
	struct complex
	{
		double rl, img;

		inline complex() {}

		inline complex(double _rl, double _img)
		{
			rl = _rl, img = _img;
		}

		inline complex friend operator +(complex a, complex b)
		{
			return complex(a.rl + b.rl, a.img + b.img);
		}

		inline complex friend operator -(complex a, complex b)
		{
			return complex(a.rl - b.rl, a.img - b.img);
		}

		inline complex friend operator *(complex a, complex b)
		{
			return complex(a.rl * b.rl - a.img * b.img, a.rl * b.img + b.rl * a.img);
		}
	}A[N << 2], B[N << 2];

	int len;
	int rev[N << 2];

	inline void initialize(int n)
	{
		len = 1;
		int tmp = 0;
		for(; len < n << 1; len <<= 1, ++ tmp);
		rev[0] = 0;
		for(int i = 1; i < len; ++ i)
			rev[i] = rev[i >> 1] >> 1 | (i & 1) << (tmp - 1);
	}

	double PI = acos(-1);

	inline void FFT(complex *a, int opt)
	{
		for(int i = 0; i < len; ++ i)
			if(rev[i] < i)
				std::swap(a[i], a[rev[i]]);
		for(int i = 2; i <= len; i <<= 1)
		{
			complex omg_i = complex(cos(2 * PI * opt / i), sin(2 * PI * opt / i));
			for(int j = 0; j < len; j += i)
			{
				complex omg = complex(1, 0);
				for(int k = j; k < j + i / 2; ++ k)
				{
					complex u = a[k], t = omg * a[k + i / 2];
					a[k] = u + t, a[k + i / 2] = u - t;
					omg = omg * omg_i;
				}
			}
		}
		if(opt == -1)
			for(int i = 0; i < len; ++ i)
				a[i].rl /= len;
	}

	inline void work(int *a, int *b, int n, int *res)
	{
		initialize(n);
		memset(A, 0, sizeof(A)), memset(B, 0, sizeof(B));
		for(int i = 0; i < n; ++ i)
			A[i] = complex(a[i], 0);
		for(int i = 0; i < n; ++ i)
			B[i] = complex(b[i], 0);
		FFT(A, 1), FFT(B, 1);
		for(int i = 0; i < len; ++ i)
			A[i] = A[i] * B[i];
		FFT(A, -1);
		memset(res, 0, sizeof(res));
		for(int i = 0; i < len; ++ i)
			res[i] = round(A[i].rl);
	}
}

int pw[N << 1];

inline void getPower()
{
	pw[0] = 1;
	for(int i = 1; i < N << 2; ++ i)
		pw[i] = (long long)pw[i - 1] * 2 % MOD;
}

namespace manacher
{
	int a[N << 1 | 1], p[N << 1 | 1];

	inline int work(char *str, int len)
	{
		memset(a, 0, sizeof(a));
		a[0] = '#';
		for(int i = 0; i < len; ++ i)
			a[i << 1 | 1] = str[i], a[i + 1 << 1] = '#';
		a[len + 1 << 1] = '#';
		len = len << 1 | 1;
		int mx = 0, id = 0;
		for(int i = 0; i < len; ++ i)
		{
			p[i] = mx > i ? std::min(mx - i, p[(id << 1) - i]) : 1;
			for(; i >= p[i] && i + p[i] < len && a[i - p[i]] == a[i + p[i]]; ++ p[i]);
			if(p[i] + i > mx)
				mx = p[i] + i, id = i;
		}
		int ans = 0;
		for(int i = 0; i < len; ++ i)
			ans = (ans + (p[i] >> 1)) % MOD;
		return ans;
	}
}

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("BZOJ3160.in", "r", stdin);
	freopen("BZOJ3160.out", "w", stdout);
	#endif
	static char str[N];
	scanf("%s", str);
	int n = strlen(str);
	static int a[N];
	static int res1[N << 2], res2[N << 2], res[N << 2];
	memset(a, 0, sizeof(a));
	for(int i = 0; i < n; ++ i)
		a[i] = str[i] == 'a';
	convolution::work(a, a, n, res1);
	memset(a, 0, sizeof(a));
	for(int i = 0; i < n; ++ i)
		a[i] = str[i] == 'b';
	convolution::work(a, a, n, res2);
	for(int i = 0; i < n << 1; ++ i)
		res[i] = res1[i] + res2[i] + 1 >> 1;
	getPower();
	int ans = 0;
	for(int i = 0; i < n << 1; ++ i)
		ans = (ans + pw[res[i]] - 1) % MOD;
	printf("%d
", (ans - manacher::work(str, n) + MOD) % MOD);
}