分析:
原来倒着读题目就是题解啊。。。。出题人很皮。。。
恩。。lucas定理在组合数学中还算是比较常见的了,之前在CQOI2018就遇到一道可以用Lucas骗分的(运气好还能卡过)的题。。。
其实lucas定理本身还是比较简单的(看着隔壁的EXLucas瑟瑟发抖。。。)
具体的证明请自行百度。。。(或者跟我一样不会证明直接用)
观察这个式子,发现其实就是把n和m按p进制分解,每一位分别求组合数,再乘起来。
而这个式子为0的条件也很容易发现
由于组合数满足:如果
所以n和m在p进制下,只要任意一位m比n大,那么值就为0.
现在看回这道题,海绵宝宝矩阵中,只有当 时,这个位置才是1,否则就是0.
可以把这个矩阵看做一个图,若 则说明从到j有一条单向边。换言之,可以将这个矩阵的每一位 看作从 走一步到 的方案数。那么这个矩阵的k次幂就表示:从 走k步到 的方案数。。。。
(卧槽这不就是之前那场的HDU6331 Walking Plan么。。。。)
然后题目非常恶心,它求的其实就是这个图中,从i出发,到达j的步数不超过k的方案数。
现在考虑怎么求答案
边
存在的条件是,
,根据lucas定理,可以按p进制下每一位独立来看。因为若从i走到j,必须每一位都满足
,它只与自己这一位有关,无法限制其他位的情况。因此每一位是互相独立的。所以只要知道某一位的方案数,然后利用乘法原理再组合起来,形成一个幂的形式,就是整个的方案数。
现在分析某一位的方案怎么求:
假设要求走x步的方案数:
其实就是[0,p-1]这些数组成长度为x+1的不上升序列的方案数。。。(因为要求每一步都不能变大)
可以利用隔板法,把[0,p-1]中的每个数看作一个空隙,在这些空隙中放x+1个隔板,隔板可以重合。每种隔板的摆放方案,就对应了一个不上升序列。(每个隔板到0的距离就表示这一位的值)
所以方案数就是
总的答案就是
后面那部分可以用等比数列求和公式算出来。前面那部分枚举就好了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define SF scanf
#define PF printf
#define EPS 1e-6
#define MAXN 200010
#define MAXM 1300000
#define MOD 1000000007
using namespace std;
typedef long long ll;
int t,n;
bool isprime[MAXM];
int primes[MAXN],cnt;
void linearsieve(){
for(int i=2;i<=MAXM&&cnt<100000;i++){
if(isprime[i]==0)
primes[++cnt]=i;
for(int j=1;i*primes[j]<=MAXM;j++){
int x=i*primes[j];
isprime[x]=1;
if(i%primes[j]==0)
break;
}
}
}
ll fac[MAXM+MAXN],ifac[MAXM+MAXN];
ll fsp(ll x,int y){
ll res=1;
while(y){
if(y&1)
res=res*x%MOD;
x=x*x%MOD;
y>>=1;
}
return res;
}
void prepare(){
fac[0]=1;
for(int i=1;i<=1500000;i++)
fac[i]=fac[i-1]*i%MOD;
ifac[1500000]=fsp(fac[1500000],MOD-2);
for(int i=1500000;i>0;i--)
ifac[i-1]=ifac[i]*i%MOD;
}
ll C(int n,int m){
return fac[n]*ifac[m]%MOD*ifac[n-m]%MOD;
}
ll solve(ll x,int n){
return (fsp(x,n)-1+MOD)%MOD*(fsp(x-1,MOD-2))%MOD*x%MOD;
}
int main(){
linearsieve();
prepare();
SF("%d",&t);
int c,n,k;
while(t--){
SF("%d%d%d",&c,&n,&k);
ll ans=0;
ll p=primes[c];
for(int i=1;i<=k;i++){
ll x=C(p+i,p-1);
if(x==1)
ans+=n;
else
ans+=solve(x,n);
ans%=MOD;
}
PF("%lld\n",ans);
}
}