芝加哥大学2016北美邀请赛Problem E:K-Inversions(FFT板题)

1 篇文章 0 订阅

题目链接

https://codeforces.ml/gym/101002

题意

有个只有AB两种字符的串,求出每个k-inversion的个数。k-inversion是长度为k,收尾分别是B,A的子串。

题解

FFT板题。设 { a i = 1 & b i = 0 , s i = ′ A ′ b i = 1 & a i = 0 , s i = ′ B ′ \left\{ \begin{array}{lr} a_i=1\&b_i=0,s_i='A'\\ b_i=1\&a_i=0,s_i='B' \end{array} \right. {ai=1&bi=0,si=Abi=1&ai=0,si=B
那么 a n s k = ∑ i = 0 , j = 0 , i + ( − j ) = = k i < n , j < n a i ∗ b j ans_k=\sum_{i=0,j=0,i+(-j)==k}^{i<n,j<n}a_i*b_j ansk=i=0,j=0,i+(j)==ki<n,j<naibj
明显是卷积的形式。所以直接吧b数组倒过来。这样i-j变成了i+n-j-1。所以k的答案是结果数组的c[n-1+k]。
AC代码可以当板子:

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const double pi=acos(-1.0);
using namespace std;
const int NN=2*1024*1014+100;
struct complex
{
	double re,im;
	complex(double r=0.0,double i=0.0) {re=r,im=i;}
	void print() {printf("%lld\n",(long long)(re+0.5));}
} a[NN*2],b[NN*2],W[2][NN*2];
int N,na,nb,rev[NN*2];

complex operator +(const complex&A,const complex&B) {return complex(A.re+B.re,A.im+B.im);}
complex operator -(const complex&A,const complex&B) {return complex(A.re-B.re,A.im-B.im);}
complex operator *(const complex&A,const complex&B) {return complex(A.re*B.re-A.im*B.im,A.re*B.im+A.im*B.re);}

void FFT(complex*a,int f)
{
	complex x,y;
	for (int i=0; i<N; i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
	for (int i=1; i<N; i<<=1)
		for (int j=0,t=N/(i<<1); j<N; j+=i<<1)
			for (int k=0,l=0; k<i; k++,l+=t) x=W[f][l]*a[j+k+i],y=a[j+k],a[j+k]=y+x,a[j+k+i]=y-x;
	if (f) for (int i=0; i<N; i++) a[i].re/=N;
}

void work()
{
	for (int i=0; i<N; i++)
	{
		int x=i,y=0;
		for (int k=1; k<N; x>>=1,k<<=1) (y<<=1)|=x&1;
		rev[i]=y;
	}
	for (int i=0; i<N; i++) W[0][i]=W[1][i]=complex(cos(2*pi*i/N),sin(2*pi*i/N)),W[1][i].im=-W[0][i].im;
}

// void init()
// {
// 	scanf("%d",&na); for (int i=0; i<na; i++) scanf("%d",&a[i].re);
// 	scanf("%d",&nb); for (int i=0; i<nb; i++) scanf("%d",&b[i].re);
// 	for (N=1; N<na||N<nb; N<<=1); N<<=1;
// }

void solve()
{
	work(),FFT(a,0),FFT(b,0);
	for (int i=0; i<N; i++) a[i]=a[i]*b[i];
	FFT(a,1);
}
char s[NN];
int main(){
    scanf("%s",s);int n=strlen(s);
    if(n==1){
        printf("0\n");
        return 0;
    }
    for(int i=0;i<n;i++){
        if(s[i]=='A'){
            a[i]=1;b[n-i-1]=0;
        }
        else{
            a[i]=0;
            b[n-i-1]=1;
        }
    }
    na=n,nb=n;
	for (N=1; N<na||N<nb; N<<=1); N<<=1;
	solve();
	for(int i=1;i<n;i++){
        a[i+n-1].print();
    }
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值