【XSY2535】整数 NTT

来源:互联网 发布:linux删除组命令 编辑:程序博客网 时间:2024/05/28 15:09

题目描述

  问有多少个满足以下要求的k进制数:

   1.每个数字出现的次数不超过n

   2.0没有出现过

   3.若gi,j=0,则i不能出现恰好j次。

  两次询问之间会修改g中一个位置的值(0110)。

  输出所有询问的答案的和。

  3k10,n14000,m20

  模数p=786433,原根g=10

题解

  假设第i个数用了ci个,答案为

(ci)!ci!

  构造多项式
fi(x)=j=0ngi,jj!xj

  把这k1个多项式乘起来后,第i项乘以i!的和就是答案。

  因为求的是答案的和,所以可以在点值表达的形式下累加答案,最后IDFT回来。

​ 怎么求没修改前的答案?

  直接DFT

  怎么求修改的贡献?

  观察NTT的公式:

yk=j=0n1aj(gp1n)kj

  对于一个单点修改操作,可以看成在某个多项式上加上一个只有一项系数不为0的多项式。这个多项式DFT后就是一个等比数列,直接加到原多项式上就完了。

  对于所有多项式的乘积:如果所有多项式的每一项都非0,就直接乘以逆元。现在有0,就记录每一项0的个数和非0的乘积。

  时间复杂度:O(nk2log(nk)+mnk)

代码

#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#include<ctime>#include<utility>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> pii;const ll p=786433;const ll g=10;ll inv[1000010];ll fac[1000010];ll ifac[1000010];ll pg[1000010];ll fp(ll a,ll b){    ll s=1;    while(b)    {        if(b&1)            s=s*a%p;        a=a*a%p;        b>>=1;    }    return s;}namespace ntt{    int n;    ll w1[150000];    ll w2[150000];    int rev[150000];    void init(int x)    {        n=1;        while(n<=x)            n<<=1;        int i;        for(i=1;i<=n;i<<=1)        {            w1[i]=fp(g,(p-1)/i);            w2[i]=inv[w1[i]];        }        rev[0]=0;        for(i=1;i<n;i++)            rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);    }    void ntt(ll *a,int t)    {        int i,j,k;        ll u,v,w,wn;        for(i=0;i<=n-1;i++)            if(rev[i]<i)                swap(a[i],a[rev[i]]);        for(i=2;i<=n;i<<=1)        {            wn=(t==1?w1[i]:w2[i]);            for(j=0;j<n;j+=i)            {                w=1;                for(k=j;k<j+i/2;k++)                {                    u=a[k];                    v=a[k+i/2]*w%p;                    a[k]=(u+v)%p;                    a[k+i/2]=(u-v)%p;                    w=w*wn%p;                }            }        }        if(t==-1)            for(i=0;i<n;i++)                a[i]=a[i]*inv[n]%p;    }}int &nn=ntt::n;char s[14010];int c[12][14010];void init(){    int i;    inv[0]=inv[1]=1;    for(i=2;i<=p-1;i++)        inv[i]=(-(p/i)*inv[p%i]%p+p)%p;    fac[0]=ifac[0]=1;    for(i=1;i<=p-1;i++)    {        fac[i]=fac[i-1]*i%p;        ifac[i]=ifac[i-1]*inv[i]%p;    }}ll ans;ll d[12][150000];ll f[150000];ll f2[150000];ll a[150000];int k,n,m;int main(){    freopen("a.in","r",stdin);    freopen("a.out","w",stdout);    init();    int i,j;    scanf("%d%d%d",&k,&n,&m);    ntt::init((k-1)*n);    pg[0]=1;    for(i=1;i<=p-2;i++)        pg[i]=pg[i-1]*g%p;    for(i=1;i<=k-1;i++)    {        scanf("%s",s);        for(j=0;j<=n;j++)            c[i][j]=s[j]-'0';    }    ans=0;    for(i=0;i<nn;i++)        f[i]=1;    for(i=1;i<=k-1;i++)    {        ll *u=d[i];        for(j=0;j<nn;j++)            u[j]=0;        for(j=0;j<=n;j++)            u[j]=c[i][j]*ifac[j]%p;        ntt::ntt(u,1);        for(j=0;j<nn;j++)        {            if(u[j]<0)                u[j]+=p;            if(u[j])                f[j]=f[j]*u[j]%p;            else                f2[j]++;        }    }    for(i=0;i<nn;i++)        if(!f2[i])            a[i]=(a[i]+f[i])%p;    int x,y;    int t;    for(t=1;t<=m;t++)    {        scanf("%d%d",&x,&y);        c[x][y]^=1;        for(i=0;i<nn;i++)            if(d[x][i])                f[i]=f[i]*inv[d[x][i]]%p;            else                f2[i]--;        ll s1=pg[((p-1)/nn*y)%(p-1)],s2=ifac[y];        if(!c[x][y])            s2=p-s2;        for(i=0;i<nn;i++)        {            d[x][i]+=s2;            if(d[x][i]>=p)                d[x][i]-=p;            s2=s2*s1%p;        }        for(i=0;i<nn;i++)        {            if(d[x][i])                f[i]=f[i]*d[x][i]%p;            else                f2[i]++;            if(!f2[i])                a[i]=(a[i]+f[i])%p;        }    }    ntt::ntt(a,-1);    for(i=1;i<nn;i++)        ans=(ans+a[i]*fac[i])%p;    ans=(ans%p+p)%p;    printf("%lld\n",ans);    return 0;}
原创粉丝点击