zoukankan      html  css  js  c++  java
  • Gnutella Chessmaster

    Link
    这是一个不需要推式子的清真做法。
    首先我们把棋盘黑白染色,得到两个新的棋盘,然后分别在两个棋盘上算方案数(r,R)再卷起来就好了。
    观察可得棋盘(B_1=(1,1,3,3,cdots),B_2=(2,2,4,4,cdots)),行数分别为(n,n-1)
    对于(B_1)运用Goldman-Joichi-White定理得到(sumlimits_{i=0}^nr_ix^{underline{n-i}}=x^{lfloorfrac n2 floor}(x+1)^{lceilfrac n2 ceil})
    对于(B_2)运用Goldman-Joichi-White定理得到(sumlimits_{i=0}^{n-1}R_ix^{underline{n-i}}=(x+2)^{lfloorfrac n2 floor}(x+1)^{lceilfrac n2 ceil-1})
    如果我们直接普通多项式转下降幂多项式就可以得到一个(O(nlog^2n))的辣鸡作法。
    我们观察普通多项式转下降幂多项式的过程:多点求值,IFDT。
    其中IFDT就是先变EGF再卷一个(e^{-x}),复杂度为(O(nlog n)),常数也很优秀。
    而我们显然不需要多点求值,右边这个明摆着就能(O(nlog n))快速多点求值,常数比上面的IFDT更加优秀。
    这样我们就可以得到两个下降幂多项式了,reverse一下就可以得到(r,R),再卷起来就好了。

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    using std::reverse;
    using u64=unsigned long long;
    const int N=262150,P=998244353;
    namespace IO
    {
        char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
        char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
        void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
        void Put(char x){*oS++=x;if(oS==oT)Flush();}
        int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
        void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put(' ');}
    }using namespace IO;
    void swap(int&x,int&y){x^=y^=x^=y;}
    int mod(int x){return x+(x>>31&P);}
    int mul(int a,int b){return (u64)a*b%P;}
    int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
    int w[N],ifac[N],r[N],lim;
    void init(int n)
    {
        int l=33-__builtin_clz(n),g(pow(3,(P-1)>>l));
        lim=1<<l,ifac[0]=w[lim>>1]=1,--l;
        for(int i=1;i<lim;++i) r[i]=(r[i>>1]>>1)|((i&1)<<l);
        for(int i=1;i<=n;++i) ifac[i]=mul(ifac[i-1],pow(i,P-2));
        for(int i=(lim>>1)+1;i<lim;++i) w[i]=mul(w[i-1],g);
        for(int i=(lim>>1)-1;i;--i) w[i]=w[i<<1];
        return ;
    }
    void NTT(int*a,int f)
    {
        if(!~f) reverse(a+1,a+lim);
        static u64 t[N];
        for(int i=0;i<lim;++i) t[r[i]]=a[i];
        for(int i=1;i<lim;i<<=1) for(int j=0,d=i<<1;j<lim;j+=d) for(int k=0,x;k<i;++k) x=t[i|j|k]*w[i|k]%P,t[i|j|k]=t[j|k]+P-x,t[j+k]+=x;
        for(int i=0;i<lim;++i) a[i]=t[i]%P;
        if(!~f) for(int i=0,p=(P-(P-1)/lim);i<lim;++i) a[i]=mul(a[i],p);
    }
    int e[N],f[N],g[N];
    int main()
    {
        int n=read();
        if(n==1) return !printf("1");
        init(n);
        for(int i=0;i<=n;++i) e[i]=i&1? P-ifac[i]:ifac[i];
        for(int i=1;i<=n;++i) f[i]=mul(mul(pow(i,n>>1),pow(i+1,(n+1)/2)),ifac[i]);
        for(int i=0;i<n;++i) g[i]=mul(mul(pow(i+2,n>>1),pow(i+1,(n-1)/2)),ifac[i]);
        NTT(e,1),NTT(f,1),NTT(g,1);
        for(int i=0;i<lim;++i) f[i]=mul(f[i],e[i]),g[i]=mul(g[i],e[i]);
        NTT(f,-1),NTT(g,-1),memset(f+n+1,0,(lim-n-1)<<2),memset(g+n,0,(lim-n)<<2),reverse(f,f+n+1),reverse(g,g+n),NTT(f,1),NTT(g,1);
        for(int i=0;i<lim;++i) f[i]=mul(f[i],g[i]);
        NTT(f,-1);
        for(int i=1;i<n*2;++i) printf("%d ",f[i]);
    }
    

    段落引用

  • 相关阅读:
    html5 返回当前地理位置的坐标点(经纬度)
    C#趣味程序---百鸡百钱
    Android开发:怎样隐藏自己的app应用
    Android Studio一些简单设置
    集群通信组件tribes之集群的消息接收通道
    Java基础实例
    如何用webbrowser获取ajax动态生成的网页的源码?
    phantomjs 下拉滚动条获取网页的全部源码
    Nodejs+定时截图+发送邮件
    关注网页的更新状况,了解最新的handsup 消息.
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12169039.html
Copyright © 2011-2022 走看看