hdu

题目大意:n个数的排列,每个数向后面第一个大于它的点连边,排列的权值为每个联通块大小的平方,求所有排列的权值和。

思路:

考虑直接设dp[i]表示n=i时的答案。

我们考虑放完前n-1个数之后再插入n,会发现n前面所有数都和它联通。

于是dp方程就出来了:

$dp[n]=\Sigma(dp[n-k]*k^{2}*(k-1)!*C_{n-1}^{k-1})$

组合数倒腾过来变成:

$\frac{dp[n]}{(n-1)!}=\Sigma(\frac{dp[n-k]}{(n-k)!}*k^{2})$

分治FFT搞定。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 typedef long long lint;
 6 const lint mo=998244353;
 7 const int N=600069;
 8 lint fpow(lint a,lint p)
 9 {
10     lint ret=1;
11     while(p)
12     {
13         if(p&1ll) (ret*=a)%=mo;
14         (a*=a)%=mo;
15         p>>=1;
16     }
17     return ret;
18 }
19 lint fac[N],ifac[N],i2[N];
20 lint dp[N];
21
22 int inv[N];
23 lint wg[N],iwg[N];
24 void ntt(lint *a,int len,int tp)
25 {
26     lint ilen=fpow(len,mo-2);
27     for(int i=0;i<len;i++) if(i<inv[i]) swap(a[i],a[inv[i]]);
28     for(int i=1;i<len;i<<=1)
29     {
30         lint w0=(~tp)?wg[i]:iwg[i];
31         for(int j=0;j<len;j+=(i<<1))
32         {
33             lint w=1;
34             for(int k=0;k<i;k++,(w*=w0)%=mo)
35             {
36                 lint w1=a[j+k],w2=w*a[j+k+i]%mo;
37                 a[j+k]=(w1+w2)%mo,a[j+k+i]=(w1-w2+mo)%mo;
38             }
39         }
40     }
41     if(tp==-1) for(int i=0;i<len;i++) (a[i]*=ilen)%=mo;
42 }
43 lint a[N],b[N],c[N];
44 void cdq(int l,int r)
45 {
46     if(l==r) {(dp[l]*=(l?fac[l-1]:1))%=mo;return;}
47     int mm=l+r>>1;
48     cdq(l,mm);
49     int len=1,pl=0,n=r-l+1;
50     while(len<=n) len<<=1,pl++;
51     for(int i=1;i<len;i++) inv[i]=(inv[i>>1]>>1)|((i&1)<<(pl-1));
52     for(int i=0;i<len;i++) a[i]=b[i]=0;
53     for(int i=0;i<mm-l+1;i++) a[i]=dp[i+l]*ifac[i+l]%mo;
54     for(int i=0;i<r-l+1;i++) b[i]=i2[i];
55     ntt(a,len,1),ntt(b,len,1);
56     for(int i=0;i<len;i++) c[i]=a[i]*b[i]%mo;
57     ntt(c,len,-1);
58     for(int i=mm+1;i<=r;i++) (dp[i]+=c[i-l])%=mo;
59     cdq(mm+1,r);
60 }
61
62
63 int xi,T;
64 int main()
65 {
66     fac[0]=ifac[0]=1;
67     for(int i=1;i<=100000;i++) fac[i]=fac[i-1]*i%mo,ifac[i]=fpow(fac[i],mo-2),i2[i]=1ll*i*i%mo;
68     for(int i=1;i<524288;i<<=1) wg[i]=fpow(3,(mo-1)/(i<<1)),iwg[i]=fpow(wg[i],mo-2);
69     dp[0]=1;
70     cdq(0,100000);
71     while(scanf("%d",&xi)!=EOF) printf("%lld\n",dp[xi]);
72     return 0;
73 }
View Code
01-21 09:55