码迷,mamicode.com
首页 > 其他好文 > 详细

P4091 [HEOI2016/TJOI2016]求和

时间:2019-02-26 10:36:21      阅读:186      评论:0      收藏:0      [点我收藏+]

标签:ini   \n   names   using   长度   algorithm   spl   cpp   ring   

留待警戒

FFT的时候长度要写的和函数里一样啊XD

瞎扯

这是个第二类斯特林数的理性愉悦颓柿子题目
颓柿子真的是让我hi到不行啦(才没有)

前置芝士

一个公式

\[ \sum_{i=0}^n t^i = \frac{t^{n+1}-1}{t-1} \]

第二类斯特林数

第二类斯特林数的是指把n个对象放到m个集合里面的方案数
其递推式是
\[ S_{n}^{m}=S_{n-1}^{m-1}+mS_{n-1}^{m} \]

容斥原理的得到的通式

\[ S_n^m=\frac{1}{m!}\sum_{i=0}^m(-1)^{i}C_{m}^i(m-i)^n \]

颓柿子

题目要求求这样一个式子
\[ f(n)=\sum_{i=0}^n\sum_{j=0}^iS_i^j\times2^j\times(j!) \]
然后我们把第二类斯特林数的通式代入进去
\[ f(n)=\sum_{i=0}^n\sum_{j=0}^iS_i^j\times2^j\times(j!) \]
得到
\[ f(n)=\sum_{i=0}^n\sum_{j=0}^i \frac{1}{j!}\sum_{k=0}^j(-1)^{k}C_{j}^k(j-k)^i\times2^j\times(j!)\\=\sum_{i=0}^n\sum_{j=0}^ij!\times2^j\sum_{k=0}^j\frac{(-1)^k}{k!}\times\frac{(j-k)^i}{(j-k)!}\\=\sum_{j=0}^nj!\times2^j\sum_{k=0}^{j}\frac{(-1)^k}{k!}\times\frac{\sum_{i=0}^n(j-k)^i}{(j-k)!} \]
如果我们设\(F(i)=\frac{(-1)^k}{k!}\)\(G(i)=\frac{\sum_{i=0}^n(j-k)^i}{(j-k)!}\),则很容易就能看出一个卷积的形式,式子变形成
\[ f(n)=\sum_{j=0}^nj!\times 2^j \sum_{i=0}^j F(i)\times G(j-i) \]
FFT求后面的式子就行了

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int MOD=998244353LL,G=3,invG=332748118;
int pow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)
            ans=(1LL*ans*a)%MOD;
        a=(1LL*a*a)%MOD;
        b>>=1;
    }
    return (ans%MOD+MOD)%MOD;
}
void FFT(int *a,int opt,int n){
    int lim=0;
    while((1<<lim)<n)
        lim++;
    for(int i=0;i<n;i++){
        int t=0;
        for(int j=0;j<lim;j++)
            if((i>>j)&1)
                t|=(1<<(lim-j-1));
        if(i<t)
            swap(a[t],a[i]);
    }
    for(int i=2;i<=n;i<<=1){
        int len=i/2;
        int tmp=pow((opt)?G:invG,(MOD-1)/i);
        for(int j=0;j<n;j+=i){
            int arr=1;
            for(int k=j;k<j+len;k++){
                int t=(a[k+len]*arr)%MOD;
                a[k+len]=((a[k]-t)%MOD+MOD)%MOD;
                a[k]=(a[k]+t)%MOD;
                arr=(arr*tmp)%MOD;
            }
        }
    }
    if(opt==0){
        int invn=pow(n,MOD-2);
        for(int i=0;i<n;i++)
            a[i]=(a[i]*invn)%MOD;
    }    
}
int a[300100],b[300100],n;
int jc[300100],inv[300100];
void init(void){
    jc[0]=inv[0]=1;
    for(int i=1;i<=n;i++){
        jc[i]=jc[i-1]*i%MOD;
        inv[i]=pow(jc[i],MOD-2);
    }
}
int f(int x){
    return ((((x&1)?-1:1)%MOD+MOD)%MOD*(inv[x]))%MOD;
}
int g(int x){
    if(x==1)
        return n+1;
    return ((((pow(x,n+1)-1)%MOD+MOD)%MOD)*pow(x-1+MOD,MOD-2)%MOD)*inv[x]%MOD;
}
signed main(){
    scanf("%lld",&n);
    // printf("n=%d\n",n);
    init();
    for(int i=0;i<=n;i++)
        a[i]=f(i),b[i]=g(i);
    // for(int i=0;i<=n;i++)
    //     printf("f(%lld)=%lld g(%lld)=%lld\n",i,a[i],i,b[i]);
    int lx=1;
    while(lx<=(n+n))
        lx<<=1;
    FFT(a,1,lx);
    FFT(b,1,lx);
    for(int i=0;i<lx;i++)
        a[i]=(a[i]*b[i])%MOD;
    FFT(a,0,lx);
    // for(int i=0;i<=n;i++)
    //     printf("f*g(%lld)=%lld\n",i,a[i]);
    int ans=0;
    for(int i=0,j=1;i<=n;i++,j=(j+j)%MOD)
        ans=(ans+j*jc[i]%MOD*a[i]%MOD)%MOD;
    printf("%lld\n",ans);
    return 0;
}

P4091 [HEOI2016/TJOI2016]求和

标签:ini   \n   names   using   长度   algorithm   spl   cpp   ring   

原文地址:https://www.cnblogs.com/dreagonm/p/10435299.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!