1. 程式人生 > >P4345 [SHOI2015]超能粒子炮·改

P4345 [SHOI2015]超能粒子炮·改

分塊 line while || 整除 itl target ostream getchar()

傳送門

看到數據和模數大小就知道要上 lucas 了

然後開始愉快地推公式:

答案為 $\sum _{i=0}^kC_{n}^{i}\ (mod\ 2333)$

設 $f [ i ] [ j ] = \sum _{k=0}^jC_{i}^{k}\ (mod\ 2333)\ ,\ P=2333$

那麽根據 lucas 定理得 $f[n][k]=\sum _{i=0}^k {C_{n\%P}^{i\%P}C_{n/P}^{i/p}}$

看到 $i/P$ 容易想到整除分塊,那就把 $i/P$ 相同的塊提出來看看

$=C_{n/P}^{0} \sum _{i=0}^{p-1}{C_{n\%P}^{i}}+C_{n/P}^{1} \sum _{i=0}^{p-1}{C_{n\%P}^{i}}+...+ C_{n/P}^{k/P}\sum _{i=0}^{k\%P}{C_{n\%P}^{i}}$

把$\sum _{i=0}^{p-1}{C_{n\%P}^{i}}$ 提出來,得到

$=\sum _{i=0}^{p-1}{C_{n\%P}^{i}}(C_{n/P}^{0}+C_{n/P}^{1}+...+C_{n/P}^{k/P-1})+ C_{n/P}^{k/P}\sum _{i=0}^{k\%P}{C_{n\%P}^{i}}$

那就可以寫成 $=f[n\%P][P-1]\cdot f[n/P][k/P-1]+ C_{n/P}^{k/P}f[n\%P][k\%P]$

然後就可以遞歸下去求了

註意long long

#include<iostream>
#include<algorithm>
#include
<cstdio> #include<cmath> #include<cstring> using namespace std; typedef long long ll; inline ll read() { ll x=0,f=1; char ch=getchar(); while(ch<0||ch>9) { if(ch==-) f=-1; ch=getchar(); } while(ch>=0&&ch<=9) { x=(x<<1)+(x<<3
)+(ch^48); ch=getchar(); } return x*f; } const int M=3007,mo=2333; inline ll fk(ll x) { return x>=mo ? x-mo : x; } int T; ll N,K,f[M][M],C[M][M]; inline ll lucas(ll a,ll b)//lucas不解釋 { if(a<b) return 0; if(!b||a==b) return 1; return C[a%mo][b%mo]*lucas(a/mo,b/mo)%mo; } inline ll F(ll n,ll k) { if(k<0) return 0; if(!n||!k) return 1;//邊界 if(n<mo&&k<mo) return f[n][k];//邊界 return fk( F(n/mo,k/mo-1)*f[n%mo][mo-1]%mo + lucas(n/mo,k/mo)*f[n%mo][k%mo]%mo ); } void pre()//預處理,註意C和f範圍不同 { C[0][0]=f[0][0]=1; for(int i=1;i<=mo;i++) { C[i][0]=C[i][i]=f[i][0]=1; for(int j=1;j<i;j++) C[i][j]=fk(C[i-1][j]+C[i-1][j-1]); } for(int i=0;i<=mo;i++) for(int j=1;j<=mo;j++) f[i][j]=fk(f[i][j-1]+C[i][j]); } int main() { pre(); scanf("%d",&T); while(T--) { N=read(); K=read(); printf("%lld\n",F(N,K)); } return 0; }

P4345 [SHOI2015]超能粒子炮·改