嘟嘟嘟




方案:回文子序列数 - 回文子串数。

回文子串数用manacher解决就行了,关键是怎么求会问序列数。

一个比较好的\(O(n ^ 2)\)的算法:对于一个回文中心\(i\),\(O(n)\)求出以\(i\)为中心位置对称且字母相同的字母对数\(x\),则以\(i\)为回文中心的回文子序列有\(2 ^ x - 1\)个(排除空序列)。

现在想一下怎么优化。

上面寻找的过程,用一个式子写出来就是这样:

\[c(i) = \sum _ {j = 0} ^ {i} s[i - j] == s[i + j]
\]

会发现这东西和卷积特别像,但是怎么用多项式表示\(s[i - j] == s[i + j]\)呢?

因为题中说了只有\(a, b\)两种字母,所以构造两个只有\(0\)或\(1\)的多项式\(A, B\),如果\(s[i] = 'a'\),那么\(A(i) = 1\);\(B\)同理。

这样的话式子就变成了

\[c(i) = \sum_{j = 0} ^ {i} A(i - j) * A(i + j) + \sum _ {j = 0} ^ {i} B(i - j) * B(i + j) - 1
\]

然后两次卷积就行啦。




fft还是不熟……

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const db PI = acos(-1);
const int maxn = 8e5 + 5;
const ll mod = 1e9 + 7;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
} int n, len = 1;
char s[maxn]; struct Comp
{
db x, y;
inline Comp operator + (const Comp& oth)const
{
return (Comp) {x + oth.x, y + oth.y};
}
inline Comp operator - (const Comp& oth)const
{
return (Comp){x - oth.x, y - oth.y};
}
inline Comp operator * (const Comp& oth)const
{
return (Comp){x * oth.x - y * oth.y, x * oth.y + oth.x * y};
}
friend inline void swap(Comp& a, Comp& b)
{
swap(a.x, b.x); swap(a.y, b.y);
}
}a[maxn], b[maxn], omg[maxn], inv[maxn]; void init()
{
omg[0] = inv[0] = (Comp){1, 0};
omg[1] = inv[len - 1] = (Comp){cos(2 * PI / len), sin(2 * PI / len)};
for(int i = 2; i < len; ++i) omg[i] = inv[len - i] = omg[i - 1] * omg[1];
}
void fft(Comp* a, Comp* omg)
{
int lim = 0;
while((1 << lim) < len) lim++;
for(int i = 0; i < len; ++i)
{
int t = 0;
for(int j = 0; j < lim; ++j) if((i >> j) & 1) t |= (1 << (lim - j - 1));
if(i < t) swap(a[i], a[t]);
}
for(int l = 2; l <= len; l <<= 1)
{
int q = l >> 1;
for(Comp* p = a; p != a + len; p += l)
for(int i = 0; i < q; ++i)
{
Comp t = omg[len / l * i] * p[i + q];
p[i + q] = p[i] - t, p[i] = p[i] + t;
}
}
} char t[maxn << 1];
int p[maxn << 1], m;
void manacher()
{
t[0] = '@';
for(int i = 0; i < n; ++i) t[i << 1 | 1] = '#', t[(i << 1) + 2] = s[i];
m = (n << 1) + 2;
t[m - 1] = '#'; t[m] = '$';
int mx = 0, id;
for(int i = 1; i < m; ++i)
{
if(mx > i) p[i] = min(p[(id << 1) - i], mx - i);
else p[i] = 1;
while(t[i - p[i]] == t[i + p[i]]) p[i]++;
if(i + p[i] > mx) mx = p[i] + i, id = i;
}
} ll quickpow(ll a, ll b)
{
ll ret = 1;
for(; b; b >>= 1, a = a * a % mod)
if(b & 1) ret = ret * a % mod;
return ret;
} ll Ans = 0;
int ans[maxn]; int main()
{
scanf("%s", s);
n = strlen(s);
while(len < (n << 1)) len <<= 1;
for(int i = 0 ; i < n; ++i) a[i] = (Comp){s[i] == 'a', 0}, b[i] = (Comp){s[i] == 'b', 0};
init();
fft(a, omg); fft(b, omg);
for(int i = 0; i < len; ++i) a[i] = a[i] * a[i] + b[i] * b[i];
fft(a, inv);
for(int i = 0; i < len; ++i) ans[i] = ((a[i].x / len + 0.5) + 1) / 2;
for(int i = 0; i < len; ++i) Ans = (Ans + quickpow(2, ans[i]) - 1) % mod;
manacher();
for(int i = 1; i < m; ++i) Ans = (Ans - (p[i] >> 1) + mod) % mod;
write(Ans), enter;
return 0;
}

luogu P4199 万径人踪灭的更多相关文章

  1. 【洛谷】P4199 万径人踪灭

    题解 每种字符跑一遍FFT,得到\(i + j = k\)时匹配的个数(要÷2,对于相同位置的最后再加上 然后算出\(2^{cnt[k]}\)的和,最后再减去用mancher匹配出的连续回文子串的个数 ...

  2. 洛咕 P4199 万径人踪灭

    给了两条限制,但是第二条想想是没用的,直接manacher就可以减掉多余的部分了,所以要求满足第一条的方案 也不难,可以想到枚举每个中心点,计算两边有多少对距离中心相等的位置值也相等,假设有\(t\) ...

  3. P4199 万径人踪灭 FFT + manacher

    \(\color{#0066ff}{ 题目描述 }\) \(\color{#0066ff}{输入格式}\) 一行,一个只包含a,b两种字符的字符串 \(\color{#0066ff}{输出格式}\) ...

  4. 洛谷P4199 万径人踪灭(manacher+FFT)

    传送门 题目所求为所有的不连续回文子序列个数,可以转化为回文子序列数-回文子串数 回文子串manacher跑一跑就行了,考虑怎么求回文子序列数 我们考虑,如果$S_i$是回文子序列的对称中心,那么只要 ...

  5. 2019.2-2019.3 TO-DO LIST

    DP P2723 丑数 Humble Numbers(完成时间:2019.3.1) P2725 邮票 Stamps(完成时间:2019.3.1) P1021 邮票面值设计(完成时间:2019.3.1) ...

  6. FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅰ

    众所周知,tzc 在 2019 年(12 月 31 日)就第一次开始接触多项式相关算法,可到 2021 年(1 月 1 日)才开始写这篇 blog. 感觉自己开了个大坑( 多项式 多项式乘法 好吧这个 ...

  7. Luogu 魔法学院杯-第二弹(萌新的第一法blog)

    虽然有点久远  还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题  沉迷游戏,伤感情 #include <queue> ...

  8. luogu p1268 树的重量——构造,真正考验编程能力

    题目链接:http://www.luogu.org/problem/show?pid=1268#sub -------- 这道题费了我不少心思= =其实思路和标称毫无差别,但是由于不习惯ACM风格的题 ...

  9. 【bzoj3160】【xsy1726】万径人踪灭

    [bzoj3160]万径人踪灭 题意 给定一个由'a'和'b'构成的字符串,求不连续回文子序列的个数. \(n\leq 100000\) 分析 还是蛮不错的. 这道题基本上是自己想到的. 除了没有利用 ...

随机推荐

  1. Hive 基础你需要掌握这些

    HDFS 中一个简单的 Join查询,是否需要撸一大串代码?我只会SQL语句 能不能入坑大数据?这里我们就来聊一聊 Hive. Hive 是什么? Hive 是一种数据仓库工具,不提供数据存储(数据还 ...

  2. Python Django migrate 报错解决办法

    1. 在现有基础上又添加一个表的时候migrate报错 migrate报错django.db.utils.OperationalError: (1050, "Table 'cmdb_eidc ...

  3. 洛谷P4783 【模板】矩阵求逆(高斯消元)

    题意 题目链接 Sol 首先在原矩阵的右侧放一个单位矩阵 对左侧的矩阵高斯消元 右侧的矩阵即为逆矩阵 // luogu-judger-enable-o2 #include<bits/stdc++ ...

  4. NodeJs -- express 快速建站

    直接上干货 1.新建一个目录来存放我们即将要做的东西: 2.win+R打开小黑框:进入到我们的目录下: 3.首先我们要全局安装Express框架:**在这一步之前要确保你本机上安装了node:** n ...

  5. HTMLcanvas矩形阵雨 - 学习笔记

    HTMLcanvas矩形阵雨 在画布上执行 获取制图环境 全屏获取屏幕宽度和屏幕高度 确定每个文字的宽度 以确定列 循环输出 定时器调用 HTML 部分 <!DOCTYPE HTML> & ...

  6. 让浏览器识别HTML5规范中的新标签

    IE8浏览器中还没有添加对HTML5新标签的支持,所以在IE8中无法直接展现HTML5新标签中的内容.庆幸的是IE8/IE7/IE6支持通过document.createElement方法产生的标签, ...

  7. Week3——书上的分析

    1.long before=System.currentTimeMills();      long after=System.currentTimeMills(); 该l两句是分别记录了开始过滤和结 ...

  8. JVM知识(二):类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码(class文件),再把字节码文件装载到JVM中,最后映射到各个内存区域中,我们的程序就可以在内存中运行了.那么问题来了,这些字节码文件是怎么装 ...

  9. java基础——队列

    目录 前言 基础 实现: 两个队列模拟一个堆栈 前言 java已经提供了堆和栈的相对应的类,这里只是模拟一下队列. 队列是一种先进先出的线性表. 基础 java5中新增加了java.util.Queu ...

  10. Oracle EBS 配置文件取值

    SELECT op.profile_option_id, tl.profile_option_name, tl.user_profile_option_name, lv.level_id, lv.文件 ...