1. 程式人生 > >CF932E Team Work——第二類斯特林數

CF932E Team Work——第二類斯特林數

 

題解

n太大,而k比較小,可以O(k^2)做

想方設法爭取把有關n的迴圈變成O(1)的式子

考慮用公式:

來替換i^k

原始的組合數C(n,i)一項,考慮能否和後面的係數分離開來,直接變成2^n處理。

之後大力推式子

考慮要消掉n,就想辦法把n往裡面放,與和n有關的項外層列舉的話,相對就不動了。可以乘法分配律把n搞定。

 

 

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using
namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=5005; const int mod=1e9+7; int s[N][N];
int C[N][N]; ll qm(ll x,ll y){ ll ret=1; while(y){ if(y&1) ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret; } ll n,k; int main(){ scanf("%lld %lld",&n,&k); if(n>k){ s[0][0]=1; for(reg i=1;i<=k;++i){ for(reg j=1
;j<=k;++j){ s[i][j]=((ll)s[i-1][j-1]+(ll)j*s[i-1][j]%mod)%mod; } } ll jie=1; ll ans=0; for(reg j=1;j<=k;++j){ jie=jie*(n-j+1)%mod; ll mi=qm(2,n-j); ans=(ans+(ll)s[k][j]*jie%mod*mi%mod)%mod; } printf("%lld",ans); }else{ C[0][0]=1; for(reg i=1;i<=n;++i){ C[i][0]=1; for(reg j=1;j<=n;++j){ C[i][j]=((ll)C[i-1][j]+C[i-1][j-1])%mod; } } ll ans=0; for(reg i=1;i<=n;++i){ ans=(ans+(ll)C[n][i]*qm(i,k))%mod; } printf("%lld",ans); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/12/28 19:46:50 */
View Code

推式子其實是下策(下下策是打表找規律。。。)

如果有組合意義的話,那麼效果是立竿見影的。

意義是,n個盒子,從中選擇i個出來,再把k個球往這i個盒子裡放,可以不放的方案數總和。盒子不同球不同

k很小,沒用的盒子很多,

轉化研究物件,

考慮k個球最終佔據了哪幾個盒子。其他的盒子打醬油愛選不選。

那麼直接就是:

一步搞定!