zoukankan      html  css  js  c++  java
  • BZOJ3160 万径人踪灭 【fft + manacher】




    题解

    此题略神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
    ",((ans1 - ans2) % P + P ) % P);
    	return 0;
    }
    
    
  • 相关阅读:
    App分享微信小程序
    PHP-FFMpeg 操作视频/音频文件 (转)
    用户画像
    phpcms中的RBAC权限系统
    PHPExcel生成excel
    OPNET中节点模型中包流的索引号的含义
    删除opnet之前保存或打开的目录后每次打开总会提示warning
    opnet 的学习方法有感
    win10:两款轻量级美化软件使用技巧(StartlsBack++与RocketDock)
    任务栏透明
  • 原文地址:https://www.cnblogs.com/Mychael/p/8352976.html
Copyright © 2011-2022 走看看