1. 程式人生 > >HDU 5322 Hope (分治NTT優化DP)

HDU 5322 Hope (分治NTT優化DP)

iso gin can tps += log spa zoj swap

題面傳送門

題目大意:

假設現在有一個排列,每個數和在它右面第一個比它大的數連一條無向邊,會形成很多聯通塊。

定義一個聯通塊的權值為:聯通塊內元素數量的平方。

定義一個排列的權值為:每個聯通塊的權值之積

求長度為$n$所有排列的權值之和,$n\leq 1e5$,$1e4$組詢問

原題面描述不清楚啊..害得我白想了30min

和ZOJ3874一樣都是排列$DP$問題

$DP$方程還是不難想的

假設現在有一個$i-1$的排列,當我們把$i$某個位置上時

$i$前面的數都會和$i$連通,$i$後面的數一定不和$i$前面的數連通

利用這個性質就可以$DP$了,定義$f[i]$表示長度為$i$的所有排列的權值之和

$f[i]$

$i$後面一共j個數可以在$i-1$個數中任選,$i$前面一共$i-j-1$個數可以任意排列,可得

$=\sum C_{i-1}^{j}f[j](i-j-1)!(i-j)^{2}$

化簡

$=(i-1)!\sum \frac{f[j]}{j!}(i-j)^{2}$

發現這是一個卷積的形式,用分治$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;x=x*x%p,y>>=1) if(y&1) ans=ans*x%p; 25 return ans; 26 } 27 28 namespace NTT{ 29 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=2;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 73 using NTT::a; using NTT::b; using NTT::c; 74 ll f[N1],g[N1],ans[N1],mul[N1],_mul[N1]; int de; 75 void CDQ(int l,int r) 76 { 77 if(r-l==1&&l>0) 78 { 79 ans[l]=mul[l-1]*f[l]%p; 80 f[l]=ans[l]*_mul[l]%p; return; 81 } 82 if(r-l<=1) return; 83 int mid=(l+r)>>1,i,len,L; 84 CDQ(l,mid); 85 for(len=1,L=0;len<(mid-l)+(r-l)-1;len<<=1,L++); 86 for(i=l;i<mid;i++) NTT::a[i-l]=f[i]; 87 for(i=0;i<(r-l);i++) NTT::b[i]=g[i]; 88 NTT::Main(len,L); 89 for(i=mid;i<r;i++) f[i]=(f[i]+NTT::c[i-l])%p; 90 NTT::clr(len); 91 CDQ(mid,r); 92 } 93 int T,n,m; 94 int que[N1]; 95 96 int main() 97 { 98 int i,j,x,y,len,L; 99 n=100001; 100 for(i=1;i<n;i++) g[i]=1ll*i*i%p; 101 mul[0]=mul[1]=_mul[0]=_mul[1]=1; 102 for(i=2;i<n;i++) mul[i]=mul[i-1]*i%p, _mul[i]=qpow(mul[i],p-2); 103 for(len=1,L=0;len<n+n-1;len<<=1,L++); 104 NTT::Pre(len,L); 105 f[0]=1; CDQ(0,n); 106 while(scanf("%d",&n)!=EOF) 107 printf("%lld\n",ans[n]); 108 return 0; 109 110 }

HDU 5322 Hope (分治NTT優化DP)