【题解】有标号的DAG计数2

[HZOI 2015] 有标号的DAG计数 II

\(I\)中DP只有一个数组,
\[ dp_i=\sum{i\choose j}2^{j(i-j)}dp_{i-j}(-1)^{j+1} \]
不会...

傻啊直接多项式球逆,借鉴一些luogu那道模板分治FFT

这里主要有个很烦人的\(ji-j^2\),现在要构造成\(j,i-j,i\)的的形式就好了,神tst告诉我们

\[ ij = \binom{i}{2} + \binom{j+1}{2} - \binom{i-j}{2}=\dfrac {i(i-1)+j(j+1)-(i-j)(i-j-1)}{2} \]
带进去,化简
\[ \frac{f_i}{i!2^\binom{i}{2}} = \sum\limits_{j=1}^i \dfrac{(-1)^{j+1}}{j!2^\binom{j}{2}} \dfrac{f_{i-j}}{(i-j)!2^\binom{i-j}{2}} \]
\(F(x)=\sum \dfrac{f_i}{i!2^\binom{i}{2}} x^i\)\(H(x)=\sum \dfrac{(-1)^{j+1}}{j!2^\binom{j}{2}}x^j\) 上式等价于
\[ F(x)=G(x)(F(x)-1) \]
直接球逆得到 \(G(x)\)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
 
 
using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
 
namespace poly{
      const int maxn=1<<18|1;
      int r[maxn];
      int savcnt;
      inline void getr(const int&len){
        if(len==savcnt)return;
        savcnt=len;
        int cnt=0;
        for(register int t=1;t<len;t<<=1) ++cnt;
        for(register int t=1;t<len;++t)
          r[t]=r[t>>1]>>1|(t&1)<<cnt>>1;
      }
      const int mod=998244353;
      const int g=3;
      inline int ksm(const int&base,const ll&p){
        register int ret=1;
        for(register ll t=p,b=base%mod;t;t>>=1,b=1ll*b*b%mod)
          if(t&1) ret=1ll*ret*b%mod;
        return ret;
      }
      const int gi=ksm(3,mod-2);
      inline void NTT(int*a,const int&len,const int&tag){
        getr(len);
        for(register int t=0;t<len;++t)
          if(r[t]>t) swap(a[t],a[r[t]]);
        int*a1,*a0,s=g;
        if(tag!=1) s=gi;
        for(register int t=1,wn;t<len;t<<=1){
          wn=ksm(s,(mod-1)/(t<<1));
          for(register int i=0;i<len;i+=t<<1){
            a1=(a0=a+i)+t;
            for(register int k=0,w=1,m;k<t;++k,++a1,++a0,w=1ll*w*wn%mod){
                  m=1ll*w**a1%mod;
                  *a1=(*a0+mod-m)%mod;
                  *a0=(*a0    +m)%mod;
                  
            }
          }
        }
        if(tag!=1)
          for(register int t=0,w=ksm(len,mod-2);t<len;++t)
            a[t]=1ll*a[t]*w%mod;
      }
      
      void INV(int*a,int*b,const int&len){
        if(len==1){b[0]=ksm(a[0],mod-2);return;}
        INV(a,b,len>>1);
        static int A[maxn],B[maxn];
        for(register int t=0;t<len<<1;++t) A[t]=B[t]=0;
        for(register int t=0;t<len;++t) A[t]=a[t],B[t]=b[t];
        NTT(A,len<<1,1);NTT(B,len<<1,1);
        for(register int t=0;t<len<<1;++t) B[t]=1ll*A[t]*B[t]%mod*B[t]%mod;
        NTT(B,len<<1,-1);
        for(register int t=0;t<len;++t) b[t]=((b[t]+b[t])%mod+mod-B[t])%mod;
        
      }
      
            
}
 
int g[1<<19|1],f[1<<19|1];
int jc[100005];
int inv[100005];
int n;
const int mod=998244353;
int main(){
      freopen("dag_count.in","r",stdin);
      freopen("dag_count.out","w",stdout);
      n=qr();
      jc[0]=1;
      inv[0]=1;
      for(register int t=1;t<=n;++t)
        jc[t]=1ll*jc[t-1]*t%mod;
      using poly::ksm;
      inv[n]=ksm(jc[n],mod-2);
      for(register int t=n-1;t;--t) inv[t]=1ll*(t+1)*inv[t+1]%mod;
      for(register int t=1;t<=n;++t){
        g[t]=1ll*ksm(ksm(2,1ll*t*(t-1)/2),mod-2)*inv[t]%mod;
        if(t&1) g[t]=mod-g[t];
      }       
      g[0]=1;
      int k=1;
      while(k<=n)k<<=1;
      poly::INV(g,f,k);
      printf("%lld\n",1ll*jc[n]*ksm(2,1ll*n*(n-1)/2)%mod*f[n]%mod);
      return 0;
}

猜你喜欢

转载自www.cnblogs.com/winlere/p/11258188.html
DAG
今日推荐