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

15 篇文章 0 订阅
5 篇文章 0 订阅

题目描述

传送门

题目大意:有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<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
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;i<n;i<<=1) ++L;
    for (int i=0;i<=n;++i)
        R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    for (int i=0;i<n;++i)
        if (i<R[i]) swap(a[i],a[R[i]]);
    for (int k=1;k<n;k<<=1)
    {
        LL wn=fast_pow(5,(Mod-1)/(k<<1));
        for (int i=0;i<n;i+=(k<<1))
        {
            LL w=1;
            for (int j=0;j<k;++j,w=w*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;i<n;++i) c[i]=a[i];
        for (int i=n;i<k;++i) c[i]=0;
        NTT(c,k,1);NTT(b,k,1);
        for (int i=0;i<k;++i) b[i]=(2-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<n;++i) b[i]=b[i]*inv%Mod;
        for (int i=n;i<k;++i) b[i]=0;
    }
}
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]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值