题解

此题略神QAQ

orz po神牛

由题我们知道我们要求出:

回文子序列数 - 连续回文子串数

我们记为ans1和ans2

ans2可以用马拉车轻松解出,这里就不赘述了

问题是ans1

我们设\(f[i]\)表示以i位置为中心的对称的字符对数,那么i位置产生的回文子序列数 = \(2^{f[i]} - 1\)

如何求?

由对称的性质,以i为对称中心的两点\(a,b\)满足\(a+b=2*i\)

我们可以设一个这样的序列:

\(c[n]\)表示以\(n/2\)位置为对称点的对称点对数【n/2若不为整数则对称中心是字符间隙】

那么有:

\(c[n] = \sum a[k]*a[n - k]\),a[k]表示k位置的字符,*运算满足当且仅当两者字符相等时为1,否则为0

我们只需要求两次fft:

①'a'位置赋值0,'b'位置赋值1,求\(c[n] = \sum a[k]*b[n - k]\)

②'a'位置赋值1,'b'位置赋值0,求\(c[n] = \sum a[k]*b[n - k]\)

两次之和即为所求,再跑一次DFT即可【我也不知道为什么可以这样,抄po神的代码】

【讲道理分开来求,然后相加应该也行】

最后ans = ans1 - ans2

真心心累。。。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<complex>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 800005,maxm = 200005,INF = 1000000000,P = 1000000007;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
return out * flag;
}
char s[maxm],t[maxm];
int RL[maxm],n;
LL ans1,ans2,F,power[maxn];
void manacher(){
s[0] = '*';
int pos = 1,mr = 1; RL[1] = 1;
for (int i = 2; i < n; i++){
if (i <= mr) RL[i] = min(RL[2 * pos - i],mr - i + 1);
else RL[i] = 1;
while (s[i + RL[i]] == s[i - RL[i]]) RL[i]++;
if (i + RL[i] - 1 >= mr) mr = i + RL[i] - 1,pos = i;
}
}
const double pi = acos(-1);
typedef complex<double> E;
E a[maxn],b[maxn];
int m,L,R[maxn];
void fft(E* a,int f){
for (int i = 0; i < n; i++) if (i < R[i]) swap(a[i],a[R[i]]);
for (int i = 1; i < n; i <<= 1){
E wn(cos(pi / i),f * sin(pi / i));
for (int j = 0; j < n; j += (i << 1)){
E w(1,0);
for (int k = 0; k < i; k++,w *= wn){
E x = a[j + k],y = w * a[j + k + i];
a[j + k] = x + y; a[j + k + i] = x - y;
}
}
}
if (f == -1) for (int i = 0; i < n; i++) a[i] /= n;
}
int main(){
scanf("%s",t + 1); int len = strlen(t + 1);
for (int i = 1; i <= len; i++) s[++n] = '#',s[++n] = t[i]; s[++n] = '#';
manacher();
for (int i = 1; i <= n; i++) ans2 = (ans2 + (RL[i] >> 1)) % P;
//cout<<ans2<<endl;
power[0] = 1; for (int i = 1; i <= n; i++) power[i] = (power[i - 1] << 1) % P;
n = len;
m = n << 1; for (n = 1; n <= m; n <<= 1) L++;
for (int i = 0; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
for (int i = 1; i <= len; i++) a[i] = (t[i] == 'a');
fft(a,1);
for (int i = 0; i < n; i++) b[i] = a[i] * a[i];
memset(a,0,sizeof(a));
for (int i = 1; i <= len; i++) a[i] = (t[i] == 'b');
fft(a,1);
for (int i = 0; i < n; i++) b[i] += a[i] * a[i];
fft(b,-1);
for (int i = 1; i < n; i++){
F = (LL)(b[i].real() + 0.5);
ans1 = (ans1 + power[F + 1 >> 1] - 1) % P;
}
//cout<<ans1<<endl;
printf("%lld\n",((ans1 - ans2) % P + P ) % P);
return 0;
}

BZOJ3160 万径人踪灭 【fft + manacher】的更多相关文章

  1. BZOJ3160:万径人踪灭(FFT,Manacher)

    Solution $ans=$回文子序列$-$回文子串的数目. 后者可以用$manacher$直接求. 前者设$f[i]$表示以$i$为中心的对称的字母对数. 那么回文子序列的数量也就是$\sum_{ ...

  2. BZOJ 3160: 万径人踪灭 [fft manacher]

    3160: 万径人踪灭 题意:求一个序列有多少不连续的回文子序列 一开始zz了直接用\(2^{r_i}-1\) 总-回文子串 后者用manacher处理 前者,考虑回文有两种对称形式(以元素/缝隙作为 ...

  3. P4199 万径人踪灭 FFT + manacher

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

  4. BZOJ3160 万径人踪灭(FFT+manacher)

    容易想到先统计回文串数量,这样就去掉了不连续的限制,变为统计回文序列数量. 显然以某个位置为对称轴的回文序列数量就是2其两边(包括自身)对称相等的位置数量-1.对称有啥性质?位置和相等.这不就是卷积嘛 ...

  5. BZOJ 3160: 万径人踪灭 FFT+快速幂+manacher

    BZOJ 3160: 万径人踪灭 题目传送门 [题目大意] 给定一个长度为n的01串,求有多少个回文子序列? 回文子序列是指从原串中找出任意个,使得构成一个回文串,并且位置也是沿某一对称轴对称. 假如 ...

  6. BZOJ3160 万径人踪灭 字符串 多项式 Manachar FFT

    原文链接http://www.cnblogs.com/zhouzhendong/p/8810140.html 题目传送门 - BZOJ3160 题意 给你一个只含$a,b$的字符串,让你选择一个子序列 ...

  7. Luogu4199 万径人踪灭 FFT、Manacher

    传送门 先不考虑”不是连续的一段“这一个约束条件.可以知道:第$i$位与第$j$位相同,可以对第$\frac{i+j}{2}$位置上产生$1$的贡献(如果$i+j$为奇数表明它会对一条缝产生$1$的贡 ...

  8. 万径人踪灭(FFT+manacher)

    传送门 这题--我觉得像我这样的菜鸡选手难以想出来-- 题目要求求出一些子序列,使得其关于某个位置是对称的,而且不能是连续一段,求这样的子序列的个数.这个直接求很困难,但是我们可以先求出所有关于某个位 ...

  9. bzoj 3160: 万径人踪灭【FFT+manacher】

    考虑正难则反,我们计算所有对称子序列个数,再减去连续的 这里减去连续的很简单,manacher即可 然后考虑总的,注意到关于一个中心对称的两点下标和相同(这样也能包含以空位为对称中心的方案),所以设f ...

随机推荐

  1. 2018.4.15 Mac系统下如何使用StartUml画好需求分析的类图 (同样适用于windows)

    Mac如何使用StartUml (同样适用于windows) 左侧边栏的英文含义及其用法 关联(Association) [关联关系]:是一种拥有的关系,它使一个类知道另一个类的属性和方法:如:老师与 ...

  2. ajax的序列化表单提交

    通过传统的 form 表单提交的方式上传文件 ? 1 2 3 4 <form id="uploadForm" action="" method=" ...

  3. Bootstrap历练实例:模态框(Modal)插件

    模态框(Modal)是覆盖在父窗体上的子窗体.通常,其目的是显示来自一个单独源的内容,可以在不离开父窗体的情况下进行一些交互,子窗体提供一些交互或信息. <!DOCTYPE html>&l ...

  4. hash join

    hash join是oracle里面一个非常强悍的功能,当做hash join时,oracle会选择一个表作为驱动表,先根据过滤条件排除不必要的数据,然后将结果集做成hash表,放入进程的hash a ...

  5. DC 课程内容

  6. json_encode() 避免转换中文

    json_encode() 避免转换中文 我们都知道,json_encode()可以将数据转换为json格式,而且只针对utf8编码的数据有效,而且在转换中文的时候,将中文转换成不可读的”\u***” ...

  7. mysql替换表中某字段的某值

    UPDATE  `cases`  SET  `case_desc` = replace(`case_desc`, 'src="//tuku-assets.m.jia.com/assets/i ...

  8. 《linux设备驱动开发详解》笔记——8阻塞与非阻塞IO

    8.1 阻塞与非阻塞IO 8.1.0 概述 阻塞:访问设备时,若不能获取资源,则进程挂起,进入睡眠状态:也就是进入等待队列 非阻塞:不能获取资源时,不睡眠,要么退出.要么一直查询:直接退出且无资源时, ...

  9. hdu 6333

    Problem Description There are n apples on a tree, numbered from 1 to n.Count the number of ways to p ...

  10. Linux: 正则表达式

    正则表达式:正规的表示法,常规的表示法(Regular Expression)正则表达式使用单个字符串来描述,匹配一系列的符合某个句发规则的字符串. 1)命令格式; grep  [正则] 字符串 文件 ...