zoukankan      html  css  js  c++  java
  • [HEOI2016/TJOI2016]求和

    前置:第二类斯特林数

    表示把(n)个小球放入(m)个不可区分的盒子的方案数

    使用容斥原理分析,假设盒子可区分枚举至少有几个盒子为空,得到通项:

    [S(n,m)=frac{1}{m!}sum_{k=0}^{m}(-1)^kinom{m}{k}(m-k)^n ]

    分析

    [f(n)=sum_{i=0}^nsum_{j=0}^iS(i,j)2^jj! ]

    注意到(S(n,m)=0quad(m>n)),因此第二个求和上限可改为(n),并代入第二类斯特林数的通项,得到

    [f(n)=sum_{i=0}^nsum_{j=0}^nfrac{1}{j!}sum_{k=0}^j(-1)^kfrac{j!}{k!(j-k)!}(j-k)^i2^jj! ]

    [=sum_{j=0}^n2^jj!sum_{k=0}^{j}frac{(-1)^k}{k!(j-k)!}sum_{i=0}^n(j-k)^i ]

    [=sum_{j=0}^n2^jj!sum_{k=0}^{j}frac{(-1)^k}{k!}frac{sum_{i=0}^n(j-k)^i}{(j-k)!} ]

    (g(j)=sum_{k=0}^{j}frac{(-1)^k}{k!}frac{sum_{i=0}^n(j-k)^i}{(j-k)!}),则(f(n)=sum_{j=0}^n2^jj!g(j))

    (a_k=frac{(-1)^k}{k!},b_k=frac{sum_{i=0}^nk^i}{k!}=frac{k^{n+1}-1}{(k-1)k!}quad (k>1))
    (g=a otimes b)
    然后先求出(g),再求出(f)

    代码

    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<iostream>
    #include<string>
    #include<vector>
    #include<list>
    #include<deque>
    #include<stack>
    #include<queue>
    #include<map>
    #include<set>
    #include<algorithm>
    #include<complex>
    #pragma GCC optimize ("O3")
    #define rg register
    using namespace std;
    template<class T> inline T read(T&x){
        T data=0;
    	int w=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
    		if(ch=='-')
    			w=-1;
    		ch=getchar();
    	}
        while(isdigit(ch))
            data=10*data+ch-'0',ch=getchar();
        return x=data*w;
    }
    typedef long long ll;
    
    const int mod=998244353,g=3;
    
    int rev[1<<18|7];
    
    inline void calrev(int lim,int l)
    {
    	rev[0]=0;
    	for(int i=1;i<lim;++i)
    		rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    }
    
    inline int qpow(int x,int k)
    {
    	int ans=1;
    	while(k)
    	{
    		if(k&1)
    			ans=(ll)ans*x%mod;
    		x=(ll)x*x%mod,k>>=1;
    	}
    	return ans;
    }
    
    inline void FFT(int*t,int lim,int type)
    {
    	for(rg int i=0;i<lim;++i)
    		if(i<rev[i])
    			swap(t[i],t[rev[i]]);
    	for(rg int i=1;i<lim;i<<=1)
    	{
    		int gn=qpow(g,(mod-1)/(i<<1));
    		if(type==-1)
    			gn=qpow(gn,mod-2);
    		for(rg int j=0;j<lim;j+=(i<<1))
    		{
    			int gi=1;
    			for(rg int k=0;k<i;++k,gi=(ll)gi*gn%mod)
    			{
    				int x=t[j+k],y=(ll)gi*t[j+i+k]%mod;
    				t[j+k]=x+y,t[j+i+k]=x-y+mod;
    				if(t[j+k]>=mod)
    					t[j+k]-=mod;
    				if(t[j+i+k]>=mod)
    					t[j+i+k]-=mod;
    			}
    		}
    	}
    	if(type==-1)
    	{
    		int inv=qpow(lim,mod-2);
    		for(rg int i=0;i<lim;++i)
    			t[i]=(ll)t[i]*inv%mod;
    	}
    }
    
    int fac[100010],inv[100010],pow2[100010];
    
    int a[1<<18|7],b[1<<18|7];
    
    int main()
    {
    	int n;
    	read(n);
    	fac[0]=inv[0]=pow2[0]=a[0]=b[0]=1;
    	for(rg int i=1;i<=n;++i)
    	{
    		fac[i]=(ll)fac[i-1]*i%mod;
    		inv[i]=qpow(fac[i],mod-2);
    		pow2[i]=pow2[i-1]<<1;
    		if(pow2[i]>=mod)
    			pow2[i]-=mod;
    		a[i]=inv[i];
    		if(i&1)
    			a[i]=mod-a[i];
    		b[i]=(ll)(qpow(i,n+1)-1)%mod*inv[i]%mod*qpow(i-1,mod-2)%mod;
    	}
    	b[1]=n+1; 
    	int lim=1,l=0;
    	while(lim<=n*2) 
    		lim<<=1,++l;
    	calrev(lim,l);
    	FFT(a,lim,1);
    	FFT(b,lim,1);
    	for(rg int i=0;i<lim;++i)
    		a[i]=(ll)a[i]*b[i]%mod;
    	FFT(a,lim,-1);
    	int f=0;
    	for(rg int i=0;i<=n;++i)
    	{
    		f+=(ll)pow2[i]*fac[i]%mod*a[i]%mod;
    		if(f>=mod)
    			f-=mod;
    	}
    	printf("%d
    ",f);
        return 0;
    }
    
    
    

    Hint

    b1不能用等比数列公式计算,必须单独处理。

    我只需要n项,但是只算到n项是错的,举例答案是3次函数,我只需要2项就只求求2项,算出来就是一个1次函数,就是错的

    总结:lim必须至少是待卷积式子长度的两倍

  • 相关阅读:
    hackrank Sorting Array of Strings
    c programming create a file
    spine unity3D(摘自博主softimagewht)
    实现鼠标双击(OnGUI)
    使用Unity NGUIInputField组件输入时发现显示为白色就是看不到字体
    NGUI制作可滚动的文本框(摘,如有侵权,联系删除)
    Unity3d 简单的小球沿贝塞尔曲线运动(适合场景漫游使用)
    MVC简单随笔
    Unity脚本自动添加注释脚本及排版格式
    树和树的分类
  • 原文地址:https://www.cnblogs.com/autoint/p/9520572.html
Copyright © 2011-2022 走看看