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

时间:2022-04-03 23:55:58

  注意到模数很小,容易想到使用卢卡斯定理,即变成一个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=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),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==?n:gcd(m,n%m);}
int T,C[P][P],S[P][P],a[],b[],f[][];
ll n,m;
int calc(ll n,ll m)
{
int t=-;
while (n) a[++t]=n%P,n/=P;
for (int i=;i<=t;i++) b[i]=m%P,m/=P;
memset(f,,sizeof(f));
f[t+][]=;
for (int i=t;~i;i--)
{
f[i][]=f[i+][]*C[a[i]][b[i]]%P;
if (b[i]) f[i][]=f[i+][]*S[a[i]][b[i]-]%P;
f[i][]=(f[i][]+f[i+][]*S[a[i]][P-]%P)%P;
}
return (f[][]+f[][])%P;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj4591.in","r",stdin);
freopen("bzoj4591.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
T=read();
for (int i=;i<P;i++)
{
C[i][]=C[i][i]=;
for (int j=;j<i;j++)
C[i][j]=(C[i-][j-]+C[i-][j])%P;
S[i][]=;
for (int j=;j<P;j++) S[i][j]=(S[i][j-]+C[i][j])%P;
}
while (T--)
{
n=read(),m=min(n,read());
printf("%d\n",calc(n,m));
}
return ;
}