【多项式】【字符串】【manachar】BZOJ3160万径人踪灭

16 篇文章 0 订阅
1 篇文章 0 订阅

分析:

还是比较板的多项式老题

这题唯一花哨一点的,就是要求回文串不能全部连续。

而我们知道,全部连续的话可以用manachar算出来。

那么剩下的就是算所有的回文子序列的方案数了。

显然fft一发,对每个位置,算出其左边和右边对称的个数,记为 f ( i ) f(i) f(i)

那么以i为中心的回文子序列数量就是 2 f ( i ) − 1 2^{f(i)}-1 2f(i)1

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 600010
#define MOD 1000000007
using namespace std;
typedef long long ll;
const double Pi=acos(-1);
struct cpx{
	double r,i;
	cpx () {}
	cpx (double r1,double i1):r(r1),i(i1) {}
	cpx operator +(const cpx &a) const {
		return cpx(r+a.r,i+a.i);
	}
	cpx operator -(const cpx &a) const {
		return cpx(r-a.r,i-a.i);	
	}
	cpx operator *(const cpx &a) const {
		return cpx(r*a.r-i*a.i,r*a.i+i*a.r);
	}
}A[MAXN],B[MAXN];
void FFT(cpx A[],int N,int flag){
	for(int i=1,j=0;i<N;i++){
		for(int d=N;j^=d>>=1,~j&d;);
		if(i<j)
			swap(A[i],A[j]);
	}
	for(int i=1;i<N;i<<=1){
		cpx wn=cpx(cos(Pi/i),flag*sin(Pi/i));	
		for(int j=0;j<N;j+=(i<<1)){
			cpx w=cpx(1,0);
			for(int k=0;k<i;k++,w=w*wn){
				cpx x=A[j+k],y=w*A[i+j+k];
				A[j+k]=x+y;
				A[i+j+k]=x-y;
			}
		}
	}
	if(flag==-1){
		for(int i=0;i<N;i++)
			A[i].r/=N;	
	}
}
void mul(cpx A[],cpx B[],int n,int m,cpx res[]){
	static cpx tmp1[MAXN],tmp2[MAXN];
	for(int i=0;i<n;i++)
		tmp1[i]=A[i];
	for(int i=0;i<m;i++)
		tmp2[i]=B[i];
	int p=1;
	while(p<=n+m)
		p<<=1;
	FFT(tmp1,p,1);
	FFT(tmp2,p,1);
	for(int i=0;i<p;i++)
		res[i]=tmp1[i]*tmp2[i];
	FFT(res,p,-1);	
	for(int i=0;i<p;i++)
		tmp1[i]=tmp2[i]=cpx(0,0);
}
int f[MAXN],g[MAXN];
char s1[MAXN],s[MAXN];
int solve(char s[],int n){
	int mid=1,pos=1;
	for(int i=1;i<n;i++){
		g[i]=min(g[2*mid-i],pos-i+1);
		while(s[i+g[i]]==s[i-g[i]]){
			g[i]++;
			mid=i;
			pos=i+g[i]-1;
		}
	}
}
ll fsp(ll x,int y){
	ll res=1;
	while(y){
		if(y&1)
			res=res*x%MOD;
		x=x*x%MOD;
		y>>=1;
	}
	return res;
}
int main(){
	int n,m;
	SF("%s",s1);
	n=strlen(s1);
	s[0]='$';
	for(int i=0;i<n;i++)
		s[++m]='#',s[++m]=s1[i];
	s[++m]='#';
	s[++m]='@';
	n=m+1;
	for(int i=0;i<n;i++)
		A[i].r=1.0*(s[i]=='a'),B[i].r=1.0*(s[i]=='b');
	mul(A,A,n,n,A);
	mul(B,B,n,n,B);
	for(int i=0;i<n;i++)
		f[i]=int(A[i*2].r+0.5)+int(B[i*2].r+0.5);
//	for(int i=0;i<n;i++)
//		PF("%d(%c) ",f[i],s[i]);
	for(int i=1;i<n;i++){
		if(i%2==0)
			f[i]++;
		f[i]/=2;
	}
	int tot=solve(s,n);
	ll ans=0;
	for(int i=1;i<n;i++){
		g[i]=g[i]>>1;
//		PF("[%d %d]\n",f[i],g[i]);
		ans=(ans+fsp(2,f[i])-1-g[i]+MOD)%MOD;
	}
	PF("%lld",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值