1. 程式人生 > >HDU 5279 YJC plays Minecraft (分治NTT優化DP)

HDU 5279 YJC plays Minecraft (分治NTT優化DP)

sca mit class limit 我們 swap algorithm clr 發現

題目傳送門

題目大意:有$n$個小島,每個小島上有$a_{i}$個城市,同一個小島上的城市互相連接形成一個完全圖,第$i$個小島的第$a_{i}$個城市和第$i+1$個小島的第$1$個城市連接,特別地,第$n$個小島的第$a_{n}$個城市和第$1$個小島的第$1$個城市連接。現在要斷掉圖中的一些邊,保證任意兩個城市只有一條路徑或者不連通,求合法的斷邊方案總數,$n,a_{i}<=1e5$

完全不會(噴血

我們對每個小島單獨討論

如果任意兩個城市只有一條路徑或者不連通,那麽這張圖只能是一個森林

定義$f[i]$表示$i$個點的完全圖的答案

我們對第$i$個點所在的樹進行討論, 設$i$點所在的樹除了$i$點還有$j$個節點,可以得到方程

$f[i]=\sum\limits_{j=0}^{i-1} C_{i-1}^{j}f[i-j-1](j+1)^{j-1}$

完全圖有標號生成樹個數是$n^{n-2}$

把上述式子展開,發現是一個卷積形式,可以用分治$NTT$求解

顯然小島間的邊至少斷一條就ok了

如果一條都不斷邊呢?

就要保證至少一個小島內的$1$號點和$a_{i}$號點不連通

我們去掉每個小島的$1$號點和$a_{i}$號點都連通的方案數就行了

這種情況的$DP$方程和上面的差不多, 設$i$點所在的樹除了$i$點和$1$號點還有$j$個節點

$g[i]=\sum\limits_{j=0}^{i-2} C_{i-1}^{j-1}f[i-j-2](j+2)^{j}$

不用分治直接$NTT$就行了

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 (1<<18)+10
  6 #define il inline
  7 #define dd double
  8 #define ld long double
  9 #define ll long long
 10 using namespace std;
 11
12 const int inf=0x3f3f3f3f; 13 const ll p=998244353; 14 int gint() 15 { 16 int ret=0,fh=1;char c=getchar(); 17 while(c<0||c>9){if(c==-)fh=-1;c=getchar();} 18 while(c>=0&&c<=9){ret=ret*10+c-0;c=getchar();} 19 return ret*fh; 20 } 21 ll qpow(ll x,ll y) 22 { 23 ll ans=1; 24 for(;y>0;x=x*x%p,y>>=1) if(y&1) ans=ans*x%p; 25 return ans; 26 } 27 28 int T,n,m; 29 namespace NTT{ 30 ll a[N1],b[N1],c[N1],Wn[N1],_Wn[N1]; 31 int r[19][N1]; 32 void Pre(int len,int L) 33 { 34 int i,j; 35 for(j=1;j<=L;j++) for(i=0;i<(1<<j);i++) 36 r[j][i]=(r[j][i>>1]>>1)|((i&1)<<(j-1)); 37 for(i=1;i<=len;i<<=1) Wn[i]=qpow(3,(p-1)/i), _Wn[i]=qpow(Wn[i],p-2); 38 } 39 void NTT(ll *s,int len,int type,int L) 40 { 41 int i,j,k; ll wn,w,t; 42 for(i=0;i<len;i++) if(i<r[L][i]) swap(s[i],s[r[L][i]]); 43 for(k=2;k<=len;k<<=1) 44 { 45 wn=(type>0)?Wn[k]:_Wn[k]; 46 for(i=0;i<len;i+=k) 47 { 48 for(j=0,w=1;j<(k>>1);j++,w=w*wn%p) 49 { 50 t=w*s[i+j+(k>>1)]%p; 51 s[i+j+(k>>1)]=(s[i+j]+p-t)%p; 52 s[i+j]=(s[i+j]+t)%p; 53 } 54 } 55 } 56 } 57 void Main(int len,int L) 58 { 59 int i,invl=qpow(len,p-2); 60 NTT(a,len,1,L); NTT(b,len,1,L); 61 for(i=0;i<len;i++) c[i]=a[i]*b[i]%p; 62 NTT(c,len,-1,L); 63 for(i=0;i<len;i++) c[i]=c[i]*invl%p; 64 } 65 void clr(int sz) 66 { 67 memset(a,0,sz<<3); 68 memset(b,0,sz<<3); 69 } 70 }; 71 72 using NTT::a; using NTT::b; using NTT::c; 73 ll F1[N1],F2[N1],f[N1],g[N1],mul[N1],_mul[N1]; 74 void CDQ(int l,int r) 75 { 76 if(r-l==1&&l) 77 { 78 F1[l]=(f[l]*mul[l-1]%p+qpow(l,l-2))%p; 79 f[l]=F1[l]*_mul[l]%p; 80 } 81 if(r-l<=1) return; 82 int mid=(l+r)>>1,i,len,L; 83 CDQ(l,mid); 84 for(len=1,L=0;len<(mid-l)+(r-l)-1;len<<=1,L++); 85 for(i=l;i<mid;i++) NTT::a[i-l]=f[i]; 86 for(i=0;i<(r-l);i++) NTT::b[i]=g[i]; 87 NTT::Main(len,L); 88 for(i=mid;i<r;i++) f[i]=(f[i]+NTT::c[i-l])%p; 89 NTT::clr(len); 90 CDQ(mid,r); 91 } 92 int v[5][N1],sz[5]; 93 94 int main() 95 { 96 scanf("%d",&T); 97 int i,j,x,y,len,L,t; 98 for(t=0;t<T;t++) 99 { 100 sz[t]=gint(); 101 for(i=1;i<=sz[t];i++) v[t][i]=gint(), n=max(n,v[t][i]); 102 } 103 for(len=1,L=0;len<n+n-1;len<<=1,L++); 104 NTT::Pre(len,L); 105 mul[0]=mul[1]=_mul[0]=_mul[1]=1; 106 for(i=2;i<=n;i++) mul[i]=mul[i-1]*i%p, _mul[i]=qpow(mul[i],p-2); 107 for(i=2,g[1]=1;i<=n;i++) g[i]=qpow(i,i-2)*_mul[i-1]%p; 108 CDQ(0,n+1); 109 for(i=0;i<=n;i++) NTT::a[i]=F1[i]*_mul[i]%p; NTT::a[0]=1; // f[j] / (j-1)! 110 for(i=2;i<=n;i++) NTT::b[i]=qpow(i,i-2)*_mul[i-2]%p; // j^(j-2) / (j-2)! NTT::b[1]=1; 111 NTT::Main(len,L); F2[1]=1; 112 for(i=2;i<=n;i++) F2[i]=mul[i-2]*NTT::c[i]%p; 113 for(t=0;t<T;t++) 114 { 115 ll ans=qpow(2,sz[t]),ret=1; 116 for(i=1;i<=sz[t];i++) ans=ans*F1[v[t][i]]%p; 117 for(i=1;i<=sz[t];i++) ret=ret*F2[v[t][i]]%p; 118 ans=(ans+p-ret)%p; 119 printf("%lld\n",ans); 120 } 121 return 0; 122 123 }

HDU 5279 YJC plays Minecraft (分治NTT優化DP)