BZOJ4591 SHOI2015超能粒子炮·改(卢卡斯定理+数位dp)

  注意到模数很小,容易想到使用卢卡斯定理,即变成一个2333进制数各位组合数的乘积。对于k的限制容易想到数位dp。可以预处理一发2333以内的组合数及组合数前缀和,然后设f[i][0/1]为前i位是否卡限制的贡献就很好dp了。为什么大家都要化式子呢。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define P 2333
ll read()
{
    ll x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int T,C[P][P],S[P][P],a[20],b[20],f[20][2];
ll n,m;
int calc(ll n,ll m)
{
    int t=-1;
    while (n) a[++t]=n%P,n/=P;
    for (int i=0;i<=t;i++) b[i]=m%P,m/=P;
    memset(f,0,sizeof(f));
    f[t+1][1]=1;
    for (int i=t;~i;i--)
    {
        f[i][1]=f[i+1][1]*C[a[i]][b[i]]%P;
        if (b[i]) f[i][0]=f[i+1][1]*S[a[i]][b[i]-1]%P;
        f[i][0]=(f[i][0]+f[i+1][0]*S[a[i]][P-1]%P)%P;
    }
    return (f[0][0]+f[0][1])%P;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4591.in","r",stdin);
    freopen("bzoj4591.out","w",stdout);
    const char LL[]="%I64d
";
#else
    const char LL[]="%lld
";
#endif
    T=read();
    for (int i=0;i<P;i++)
    {
        C[i][0]=C[i][i]=1;
        for (int j=1;j<i;j++)
        C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
        S[i][0]=1;
        for (int j=1;j<P;j++) S[i][j]=(S[i][j-1]+C[i][j])%P;
    }
    while (T--)
    {
        n=read(),m=min(n,read());
        printf("%d
",calc(n,m));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Gloid/p/9899949.html