7.28 C幸运数字
|
问题描述
每个人都会有幸运数字,有种幸运数字是这样定义的:
如果 X 是幸运数字,则 X 在 m 进制下的表示为 x1x2...xk,一定有 x1<=x2<=...<=xk,其 中 k 可以表示 X 在 m 进制下的位数。
这样的数字可能有无穷多个的,但是如果是在 m 进制下位数不超过 n 的幸运数字,就 应该是有限个了,你能算出来吗?
这个答案可能很大,你只需要输出答案对一个数 p 取模的值即可。
输入格式
共一行,三个正整数 n、m 和 p,保证 p 是质数。
输出格式
共一行,表示答案对 p 取模的值。
样例输入 1
4 3 23
样例输出 1
15
样例输入 2
4 10 10000079
样例输出 2
715
提示
前 20%的数据满足 n <= 18, m <= 10。
前 40%的数据满足 n <= 100, m <= 100。
前 60%的数据满足 n <= 1000, m <= 1000。
100%的数据满足 n <= 107, m <= 107, n + m <= p, p <= 10000079。
样例说明
样例 1 的 15 种方案如下:
0000,0001,0011,0111,1111,0002,0022,0222,2222,0012,0122,1222,1112,1122,1222
分析: 这题有一个很显然的DP 考试的时候由于纠结B题太久 没有打前缀和优化 掉了40分
20%搜索即可50%DP,f[i][j]表示以 j 结尾,长度为 i 的数字的个数。则 f[i][j]=sum{f[i-1][k]},k<=j,f[1][j]=1,复杂度 O(n^3)80%注意到前面求 f[i][j]时有求前缀和,那么用 g[i][j]表示 f[i][1]到 f[i][j]的和,则 dp 可以优化到O(nm)100%80 分的算法中,f[i][j]=g[i-1][j],也可以写成 g[i][j]-g[i][j-1]=g[i-1][j],即 g[i][j]=g[i-1][j]+g[i][j-1],这类似于一个杨辉三角形,并且发现边界 g[1][i]=i 恰好是杨辉三角形去掉最外层 1 的一边,因此可以使用组合数的方法进行直接计算 g[i][j]的值:g[i][j]=c(i,i+j-1);最终答案:ans=sum(g[i][m-1]),1<=i<=n根据 g[i][j]=g[i-1][j]+g[i][j-1]化简可以得到ans=g[n][m]=c(n,n+m-1)
至于g[i][[j] 为啥会得到C(i,i+j-1) 我只能说靠经验
然后分享一波 lucas定理板子
C(N,M)=C(N%P,M%P)*lucas(N/P,M/P);
虽然说n+m<=p 用不到lucas
code:
// #include<bits/stdc++.h> using namespace std; #define ll long long ll n,m,p; ll ksm(ll a,ll b,ll c) { ll ans=1; while(b) { if(b&1) ans=ans%p*(a%p); b>>=1; a=(a*a)%c; } return ans%p; } ll c(int n,int m) { if(n<m) return 0; if(n==m) return 1; if(n-m<m) m=n-m; ll A=1,B=1; for(int i=0;i<m;i++) { A=(A%p)*(n-i)%p; B=(B%p)*(m-i)%p; } return ksm(B,p-2,p)*A; } ll lucas(int n,int m,int p) { if(m==0) return 1; else return lucas(n/p,m/p,p)%p*c(n%p,m%p)%p; } int main() { // freopen("lucknum.in","r",stdin); // freopen("lucknum.out","w",stdout); scanf("%lld%lld%lld",&n,&m,&p); printf("%lld",lucas(n+m-1,n,p)%p); }
刀剑映出了战士的心。而我的心,漆黑且残破