[Luogu P4199] [BZOJ 3160] 万径人踪灭

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82930001

洛谷传送门

BZOJ传送门

img

img

img

解题分析

很容易想到: 方案数 = = 回文序列个数-回文串个数。

回文串我们用 m a n a c h e r manacher 搞出来就好了, 关键是回文序列数。

联系到我们写通配符匹配时的套路, 如果第 i i 个字符和第 j j 个字符相同,我们以 i + 1 2 \frac{i+1}{2} 为中心情况的贡献 c n t [ i ] cnt[i] 就会 + 1 +1

这个分数很讨厌, 我们就把这个贡献加在 i + j i+j 就好, 最后答案就为 i = 1 n × 2 ( 2 c n t [ i ] 1 ) \sum_{i=1}^{n\times 2}(2^{cnt[i]}-1) (需要去掉空序列的情况)。

c n t [ ] cnt[] 这玩意显然是个卷积的形式, 可以通过自己和自己 F F T FFT 得到, 完美 A C AC

注意计算的时候加上 E P S EPS , 误差较大。

代码如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 800500
#define db double
#define MOD 1000000007
#define EPS 0.1
const db PI = std::acos(-1.0);
struct Complex {db im, re;} a[MX], b[MX];
IN Complex operator * (const Complex &x, const Complex &y) {return {x.im * y.re + x.re * y.im, x.re * y.re - x.im * y.im};}
IN Complex operator + (const Complex &x, const Complex &y) {return {x.im + y.im, x.re + y.re};}
IN Complex operator - (const Complex &x, const Complex &y) {return {x.im - y.im, x.re - y.re};}
int rev[MX], ans[MX], pw[MX], cnt[MX];
int len, mid, pos, tot, lg, res;
char buf[MX], val[MX];
IN void FFT(Complex *dat, const int &typ)
{
	for (R int i = 0; i < tot; ++i) if(rev[i] > i) std::swap(dat[rev[i]], dat[i]);
	R int seg, bd, now, cur, step;
	Complex base, deal, buf1, buf2;
	for (seg = 1; seg < tot; seg <<= 1)
	{
		base = {std::sin(PI / seg) * typ, std::cos(PI / seg)}; step = seg << 1;
		for (now = 0; now < tot; now += step)
		{
			deal = {0, 1}, bd = now + seg;
			for (cur = now; cur < bd; ++cur, deal = deal * base)
			{
				buf1 = dat[cur], buf2 = dat[cur + seg] * deal;
				dat[cur] = buf1 + buf2, dat[cur + seg] = buf1 - buf2;
			}
		}
	}
}
IN int manacher()
{
	int ret = 0, bd = (len << 1) + 1;
	val[0] = '$'; val[1] = '#';
	for (R int i = 2; i <= bd; ++i)
	if(i & 1) val[i] = '#'; else val[i] = buf[(i >> 1) - 1];
	mid = 0, pos = 0;
	for (R int i = 1; i <= bd; ++i)
	{
		if(i < pos) ans[i] = std::min(ans[2 * mid - i], pos - i);
		else ans[i] = 1;
		W (val[i + ans[i]] == val[i - ans[i]]) ++ans[i];
		if(pos < ans[i] + i) mid = i, pos = ans[i] + i;
		ret = (ret + ans[i] / 2) % MOD;
	}
	return ret;
}
int main(void)
{
	scanf("%s", buf); len = std::strlen(buf);
	for (tot = pw[0] = 1; tot < len; tot <<= 1, ++lg); ++lg, tot <<= 1;
	for (R int i = 1; i < tot; ++i) pw[i] = pw[i - 1] * 2 % MOD;
	for (R int i = 1; i < tot; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
	for (R int i = 0; i < len; ++i) (buf[i] == 'a') ? (a[i].re = 1) : (b[i].re = 1);
	FFT(a, 1), FFT(b, 1);
	for (R int i = 0; i < tot; ++i) a[i] = a[i] * a[i], b[i] = b[i] * b[i];
	FFT(a, -1), FFT(b, -1);
	for (R int i = 0; i < tot; ++i) cnt[i] = (((int)((a[i].re + b[i].re) / tot + EPS) + 1) / 2);
	for (R int i = 0; i < tot; ++i) res = (res + pw[cnt[i]] - 1) % MOD;
	printf("%d", (res - manacher() + MOD) % MOD);
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/82930001