zoukankan      html  css  js  c++  java
  • 【题解】BZOJ5093图的价值(二项式+NTT)

    【题解】BZOJ5093图的价值(二项式+NTT)

    今天才做这道题,是我太弱了

    强烈吐槽c++这种垃圾语言tmd数组越界不re反倒去别的数组里搞事情我只想说QAQ

    推了一张A4纸的式子

    考虑每个点的度数,因为每个点虽然有标号但是是等价的,对于每个点,对于答案的贡献是(x),答案输出(n imes x)就好了,所以答案是

    [nsum_{i=1}^{n-1} i^{k} {n-1choose i}2^{frac {n(n-1)} 2-(n-1)} ]

    顺次解释:度数(^k),选择(i)个别的点去连接,剩下的边随便连

    无关项提出来

    [n2^{frac {n(n-1)} 2-(n-1)}sum_{i=1}^{n-1} i^{k} {n-1choose i} ]

    现在就是要求

    [sum_{i=1}^{n-1} i^{k} {n-1choose i} ]

    自然幂数和公式

    [i^k=sum_{j=0}^{min{i,k}} {krace j}egin{pmatrix} i \jend{pmatrix}j! ]

    套进去

    [sum_{i=1}^{n-1} sum_{j=0}^{min{i,k}} {krace j}egin{pmatrix} i \jend{pmatrix}j! {n-1choose i} ]

    先枚举(j)

    [sum_{j=0}^{n-1}sum_{i=j}^{n-1}{krace j}j!{n-1choose i}{ichoose j} ]

    整理

    [sum_{j=0}^{n-1}{krace j}j!sum_{i=j}^{n-1}{n-1choose i}{ichoose j} ]

    套一下公式(备胎模型)

    [sum_{j=0}^{n-1}{krace j}j!sum_{i=j}^{n-1}{n-1-jchoose j}{n-1-jchoose i-j} ]

    又可以提出来

    [sum_{j=0}^{n-1}{krace j}{n-1-jchoose j}j!sum_{i=j}^{n-1}{n-1-jchoose i-j} ]

    稍微改变一下形式

    [sum_{j=0}^{n-1}{krace j}{n-1-jchoose j}j!sum_{c=i-j=0}^{c=i-jle n-1-j}{n-1-jchoose c} ]

    二项式定理套

    [sum_{j=0}^{n-1}{krace j}{n-1-jchoose j}j!2^{n-1-j} ]

    我们晓得当(k > j)时式子的值(=0),所以枚举到(min {n-1,k})就好了。问题在于如何快速求那个斯特林数

    斯特林数的容斥式

    [{krace j}=dfrac 1{j!} sum_{i=0}^{j-1} (-1)^i{egin{pmatrix}j\iend{pmatrix}}(j-i)^{k} ]

    拆拆又是一个NTT的式子,不赘述了,看上面那个链接博客里有

    答案式子

    [n2^{frac {n(n-1)} 2-(n-1)}sum_{j=0}^{n-1}{krace j}{n-1-jchoose j}j!2^{n-1-j} ]

    指数上取膜,又是欧拉定理

    写一下NTT就好了

    //@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<<19|1;
          int a[maxn],b[maxn],r[maxn];
          int savlen;
          inline void getr(const int&len){
    	    if(len==savlen)return;
    	    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(int base,int p){
    	    register int ret=1;
    	    for(base%=mod;p;p>>=1,base=1ll*base*base%mod)
    		  if(p&1) ret=1ll*ret*base%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=1;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 j=0,w=1,tm;j<t;++j,++a1,++a0,w=1ll*w*wn%mod){
    			      tm=1ll**a1*w%mod;
    			      *a1=(*a0-tm)%mod;
    			      *a0=(*a0+tm)%mod;
    			      if(*a1<0)*a1+=mod;
    			}
    		  }
    	    }
    	    if(tag!=1)
    		  for(register int t=0,in=ksm(len,mod-2);t<len;++t)
    			a[t]=1ll*a[t]*in%mod;
          }
    }
    
    
    using poly::mod;
    using poly::NTT;
    using poly::ksm;
    const int maxn=2e5+5;
    int jc[maxn],inv[maxn];
    int t1[1<<19|1];
    int s[1<<19|1];
    int n,k,L,len;
    int ret,ans;
    
    inline void pre(){
          jc[0]=inv[0]=1;
          for(register int t=1;t<maxn;++t)
    	    jc[t]=1ll*jc[t-1]*t%mod;
          inv[maxn-1]=ksm(jc[maxn-1],mod-2);
          for(register int t=maxn-2;t;--t){
    	    inv[t]=1ll*inv[t+1]*(t+1)%mod;
    	    //if(t<15)cout<<"qaq="<<inv[t]<<endl;
    	    if(inv[t]==0) return void(cout<<"t="<<t<<endl);
          }
    }
    
    inline int c(const int&n,const int&m){
          if(n<m)return 0;
          return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
    }
    
    
    int main(){
          pre();
          n=qr();k=qr();
          L=min(n-1,k);
          len=1;
          while(len<=L)len<<=1;
          for(register int t=0;t<=L;++t){
    	    s[t]=inv[t];
    	    if(t&1) s[t]=mod-s[t];
    	    t1[t]=1ll*inv[t]*ksm(t,k)%mod;
          }
    
          
          NTT(t1,len<<1,1);NTT(s,len<<1,1);
          
          for(register int t=0;t<len<<1;++t) s[t]=1ll*s[t]*t1[t]%mod;
          NTT(s,len<<1,-1);
          for(register int t=k+1;t<len<<1;++t) s[t]=0;
          int p=(1ll*n*(n-1ll)/2%(mod-1)-n+1+mod-1)%(mod-1);
          ret=1ll*ksm(2,p)*(n%mod)%mod;
          int w=1;
          for(register int t=0;t<=L;++t){
    	    ans=(ans+1ll*jc[t]*w%mod*ksm(2,n-1-t)%mod*s[t]%mod)%mod;
    	    w=1ll*w*(n-1-t)%mod*inv[t+1]%mod*jc[t]%mod;
          }
          cout<<1ll*ret*ans%mod<<endl;
          return 0;
    }
    
    
  • 相关阅读:
    Object类学习
    Thread.State 线程状态
    Thread.UncaughtExceptionHandler
    apply和call的区别
    如何实现border-width:0.5px;
    table固定头部,表格tbody可上下左右滑动
    canvas画布实现手写签名效果
    ES6学习笔记
    for循环中执行setTimeout问题
    javaScript函数提升及作用域
  • 原文地址:https://www.cnblogs.com/winlere/p/11190351.html
Copyright © 2011-2022 走看看