[BZOJ2259]异化多肽(生成函数+NTT+多项式求逆)

题目描述

传送门

题目大意:有M种氨基酸,每个的相对分子质量为Ci,问组合出的肽链水解之后有多少个相对分子质量和为N(排列)。

题解

选的氨基酸的数量不限。
一个比较显然的思路是对于每一种氨基酸搞一个生成函数出来,也就是x^(Ci的倍数)项的系数为1,然后把M个卷起来就行了
但是这样复杂度是 O(MNlogN) 的,并且没法加速

换一种思路…
将所有的氨基酸搞成一个生成函数,x^k的系数为相对分子质量为k的氨基酸有多少个,设为 A(x)
那么其实就是这样一个卷积

B(x)=1+A(x)+A2(x)+A3(x)+...=11A(x)

只要求出来 1A(x) 的逆就是答案了

代码

#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define Mod 1005060097
#define N 600005

int sum,cnt,Max,n,m,L,R[N];
LL a[N],b[N],c[N];

LL fast_pow(LL a,int p)
{
    LL ans=1;
    for (;p;p>>=1,a=a*a%Mod)
        if (p&1)
            ans=ans*a%Mod;
    return ans;
}
void NTT(LL a[N],int n,int opt)
{
    L=0;for (int i=1;i1) ++L;
    for (int i=0;i<=n;++i)
        R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    for (int i=0;iif (ifor (int k=1;k1)
    {
        LL wn=fast_pow(5,(Mod-1)/(k<<1));
        for (int i=0;i1))
        {
            LL w=1;
            for (int j=0;j*wn%Mod)
            {
                LL x=a[i+j],y=w*a[i+j+k]%Mod;
                a[i+j]=(x+y)%Mod,a[i+j+k]=(x-y+Mod)%Mod;
            }
        }
    }
    if (opt==-1) reverse(a+1,a+n);
}
void inverse(int n,LL a[N],LL b[N],LL c[N])
{
    if (n==1) b[0]=fast_pow(a[0],Mod-2);
    else
    {
        inverse(n>>1,a,b,c);
        int k=n<<1;
        for (int i=0;ifor (int i=n;i0;
        NTT(c,k,1);NTT(b,k,1);
        for (int i=0;i2-c[i]*b[i]%Mod+Mod)%Mod*b[i]%Mod;
        NTT(b,k,-1);
        LL inv=fast_pow(k,Mod-2);
        for (int i=0;i*inv%Mod;
        for (int i=n;i0;
    }
}
int main()
{
    freopen("polypeptide.in","r",stdin);
    freopen("polypeptide.out","w",stdout);
    scanf("%d%d",&sum,&cnt);++a[0];
    for (int i=1;i<=cnt;++i)
    {
        int x;scanf("%d",&x);
        --a[x];Max=max(Max,x);
    }
    m=Max<<1;
    for (n=1;n<=m;n<<=1) ++L;
    for (int i=0;i<=n;++i) a[i]=(a[i]+Mod)%Mod;
    inverse(n,a,b,c);
    printf("%lld\n",b[sum]);
}

你可能感兴趣的:(题解,FFT/NTT,生成函数)