P4345 [SHOI2015]超能粒子炮·改

传送门

看到数据和模数大小就知道要上 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;
}

 

posted @ 2018-11-06 11:40  LLTYYC  阅读(164)  评论(0编辑  收藏  举报