链接:http://acm.hdu.edu.cn/showproblem.php?pid=6372
题目倒过来就是题解什么的真是太过分了一定要写博客嘤嘤嘤
首先是卢卡斯定理
C(n,m) mod p = C(n%p,m%p)*C(n/p,m/p)mod p
一般是用来解决大数求组合数的【C(n/p,m/p)可以继续用卢卡斯定理进行简化】
但同时不停的整除q和对q求余有没有让你想起 十进制数转q进制数。。。。。
所以也可以看成C(N,M)等于把n,m看成p进制数后,对应位上数字的组合数的乘积
所以C(i,j)>0 那么就意味着 在p进制下,i的每一位都>=j的对应位上的数字(不然c(i,j)=0最后总的组合数乘积为0)
然后这个矩阵A可以看成图的邻接矩阵,A[i][j]就为从i走一步到j的方案数,A的k次方就为从 i走k步到j的方案数
同时如果满足在p进制下,i的每一位数字都比j的对应位上的数字大于等于的话 那么对应的图里有一条从i->j的边
然后我们要求的 F[n][k] 是A^k的所有元素和,答案就是在图中走k步的方案数
矩阵的下标是从0到 p^n-1对应的是n位p进制数,在图中走k步意味着要找一个长度为k+1的序列,要求每个数字(n位q进制数)的每一位都大于等于上一个数字的对应位。这n位互相之间不影响,所以对于每一位是找一个长度为k+1的每个元素大于等于0小于p的不下降序列,插板法可得有C(k+p,p-1)种方法,有n位那么就是 F[n][k]=C(k+p,p-1)^n种方法
代码:
#include
#define ll long long
using namespace std;
const int MX=13e5+7;
const int mod=1e9+7;
int prime[MX],tt;
bool no_prime[MX];
ll inv[MX],fac[MX];
ll q_pow(ll a,ll b,ll mod){
ll ret=1;
a%=mod;
while(b){
if (b&1)ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
void init(){
int n=MX-7;
for (int i=2;i<=n;i++){
if (!no_prime[i])prime[++tt]=i;
for (int j=1;j<=tt&&i*prime[j]<=n;j++){
no_prime[i*prime[j]]=1;
if(!i%prime[j])break;
}
}
int p=prime[100000];
fac[0]=fac[1]=inv[0]=inv[1]=1;
for (int i=2;i<=p;i++)fac[i]=fac[i-1]*i%mod;
inv[p]=q_pow(fac[p],mod-2,mod);
for (int i=p;i>2;i--)inv[i-1]=inv[i]*i%mod;
}
void solve(){
int c,n,k;
scanf("%d%d%d",&c,&n,&k);
ll p=prime[c];
ll f=fac[p],ans=0;
for (int j=1;j<=k;j++){
f=f*(p+j)%mod;
ll a=f*inv[p-1]%mod*inv[j+1]%mod;
if (a>1)ans=((ans+(q_pow(a,n+1,mod)-a+mod)*q_pow(a-1,mod-2,mod)))%mod;
else ans=(ans+n)%mod;
}
printf("%lld\n",ans);
}
int main(){
init();
int t;
scanf("%d",&t);
while(t--)solve();
return 0;
}
总结下就是 加深了卢卡斯定理的运用
还有邻接矩阵的k次方的含义这点
菜鸡落泪orz