• bzoj4555 [Tjoi2016&Heoi2016]求和


    Description

    在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心。

    现在他想计算这样一个函数的值:

    S(i, j)表示第二类斯特林数,递推公式为:
    S(i, j) = j ∗ S(i − 1, j) + S(i − 1, j − 1), 1 <= j <= i − 1。
    边界条件为:S(i, i) = 1(0 <= i), S(i, 0) = 0(1 <= i)
    你能帮帮他吗?

    Input

    输入只有一个正整数

    Output

     输出f(n)。由于结果会很大,输出f(n)对998244353(7 × 17 × 223 + 1)取模的结果即可。1 ≤ n ≤ 100000

    Sample Input

    3

    Sample Output

    87

    正解:组合数学+分治$FFT$。

    很久以前看到这道题,当时连第二类斯特林数是什么都不知道,于是什么都不会。现在想想,还是不难的。。

    我们考虑第二类斯特林数的意义:把$n$个球放入$m$个无区别的盒子里的方案数。

    上式有一个$m!$,那就是把无区别的盒子变成有区别的盒子,$2^{m}$表示把盒子染成黑白两色。

    那么我们考虑$g[n]$为把$n$个球分成任意个有区别的集合,且每个集合任意染黑白两色的方案数。显然,$Ans=sum_{i=0}^{n}g(i)$。

    根据意义,我们可以推出$g(n)$的递推式:$g(n)=sum_{i=1}^{n}2*inom{n}{i}*g(n-i)$。

    枚举第$1$个集合有多少元素,且颜色是什么,就可以推出上式了。

    然后我们直接用分治$FFT$就可以求出$g$函数了。

     1 #include <bits/stdc++.h>
     2 #define il inline
     3 #define RG register
     4 #define ll long long
     5 #define rhl (998244353)
     6 #define N (500010)
     7 #define G (3)
     8 
     9 using namespace std;
    10 
    11 int f[N],g[N],w[N],fac[N],ifac[N],inv[N],rev[N],a[N],b[N],n,ans;
    12 
    13 il int gi(){
    14   RG int x=0,q=1; RG char ch=getchar();
    15   while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    16   if (ch=='-') q=-1,ch=getchar();
    17   while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
    18   return q*x;
    19 }
    20 
    21 il int qpow(RG int a,RG int b){
    22   RG int ans=1;
    23   while (b){
    24     if (b&1) ans=1LL*ans*a%rhl;
    25     a=1LL*a*a%rhl,b>>=1;
    26   }
    27   return ans;
    28 }
    29 
    30 il void pre(){
    31   fac[0]=ifac[0]=fac[1]=ifac[1]=inv[1]=1,g[1]=2;
    32   for (RG int i=2;i<=n;++i){
    33     inv[i]=1LL*(rhl-rhl/i)*inv[rhl%i]%rhl;
    34     fac[i]=1LL*fac[i-1]*i%rhl;
    35     ifac[i]=1LL*ifac[i-1]*inv[i]%rhl;
    36     g[i]=ifac[i]<<1; if (g[i]>=rhl) g[i]-=rhl;
    37   }
    38   for (RG int i=1,v=0;i<(n<<2);i<<=1,++v) w[v]=qpow(G,(rhl-1)/(i<<1));
    39   return;
    40 }
    41 
    42 il void NTT(int *a,RG int n,RG int f){
    43   for (RG int i=0;i<n;++i) if (i<rev[i]) swap(a[i],a[rev[i]]);
    44   for (RG int i=1,v=0;i<n;i<<=1,++v){
    45     RG int gn=w[v],x,y;
    46     for (RG int j=0;j<n;j+=i<<1){
    47       RG int g=1;
    48       for (RG int k=0;k<i;++k,g=1LL*g*gn%rhl){
    49     x=a[j+k],y=1LL*g*a[j+k+i]%rhl;
    50     a[j+k]=x+y; if (a[j+k]>=rhl) a[j+k]-=rhl;
    51     a[j+k+i]=x-y; if (a[j+k+i]<0) a[j+k+i]+=rhl;
    52       }
    53     }
    54   }
    55   if (f==1) return; reverse(a+1,a+n); RG int inv=qpow(n,rhl-2);
    56   for (RG int i=0;i<n;++i) a[i]=1LL*a[i]*inv%rhl; return;
    57 }
    58 
    59 il void solve(RG int l,RG int r){
    60   if (l==r){ if (!l) f[l]=1; return; }
    61   RG int mid=(l+r)>>1,len,lg=0; solve(l,mid);
    62   for (len=1;len<=r-l+1;len<<=1) ++lg;
    63   for (RG int i=0;i<len;++i) rev[i]=rev[i>>1]>>1|((i&1)<<(lg-1)),a[i]=b[i]=0;
    64   for (RG int i=l;i<=mid;++i) a[i-l]=f[i];
    65   for (RG int i=0;i<=r-l;++i) b[i]=g[i]; NTT(a,len,1),NTT(b,len,1);
    66   for (RG int i=0;i<len;++i) a[i]=1LL*a[i]*b[i]%rhl; NTT(a,len,-1);
    67   for (RG int i=mid+1;i<=r;++i){
    68     f[i]+=a[i-l]; if (f[i]>=rhl) f[i]-=rhl;
    69   }
    70   solve(mid+1,r); return;
    71 }
    72 
    73 int main(){
    74 #ifndef ONLINE_JUDGE
    75   freopen("sum.in","r",stdin);
    76   freopen("sum.out","w",stdout);
    77 #endif
    78   n=gi(),pre(),solve(0,n);
    79   for (RG int i=0;i<=n;++i)
    80     ans=(ans+1LL*f[i]*fac[i])%rhl;
    81   printf("%d
    ",ans); return 0;
    82 }
  • 相关阅读:
    android入门之三【应用程序组成】
    Palm应用开发之一开发环境搭建
    android 入门之一【开发环境搭建】
    在DataGridView中的CheckBox值变更后立即获取值。
    根据字符串返回类型
    CSS模拟不同的拐角效果
    SQL查询生成交叉列表
    LinkButton 的 OnClick 事件 可以是一个方法
    代替marquee的滚动字幕效果代码
    JavaScript实现DataGrid中添加CheckBox列(全选与否)
  • 原文地址:https://www.cnblogs.com/wfj2048/p/7414031.html
Copyright © 2020-2023  润新知