统计 (dp 求n位无前导零且数码从高到低非降并模 m等于 0的数个数 )
来源:互联网 发布:域名还要买 编辑:程序博客网 时间:2024/05/17 05:06
统计
【问题描述】
求n位无前导零且数码从高到低非降并模 m等于 0的数个数。
【输入格式】
两行个整数 n, m;
【输出格式】
一行个整数代表答案模 10^9+7之后的值 。
【样例输入1】
2
12
【样例输出1】
4
【样例输入2】
3
111
【样例输出2】
9
【样例输入3】
452
10
【样例输出3】
0
【样例输入4】
6
58
【样例输出4】
38
【数据规模与约定】
对于 100%的数据, 1≤n≤10^18, 1≤m≤500,有各种部分 。
思路:
神奇的dp,%大佬,get代码,add注释
#include <cstdio> #include <cstring> #include <algorithm>#include <iostream>#define LL long longusing namespace std; const int M=520, P=1000000007;LL n;int m,lst[M],cnt[M],inv[10],f[9][M],C[M][9],g[M][9],u[9][9];//考虑ans的性质,非下降,位数又是固定的,所以我们可以把ans分成最多九个形如11111..的数//eg:12345 = 11111 + 1111 + 111 + 11 + 1; 155579 = 111111 + 11111 + 11111 + 11111 + 11111 + 11 + 11 + 1 + 1;//因为ans的位数固定为n;所以最下面一层一定是n个1这个数。 //那么我们就可以算这类型的数可以组成多少符合条件的数。 int main(){ freopen ("c.in", "r", stdin); freopen ("c.out", "w", stdout); inv[1] = 1; for(int i=2; i<=8; i++)//预处理1~8的逆元 inv[i] = (LL)inv[P%i] * (P-P/i)%P;//递推求法 /*for(int i=1; i<=8; i++) cout << inv[i] << endl;*/ scanf("%I64d%I64d", &n, &m); //n = 15, m = 12; int v=0; for(int i=1; i<=n; i++){ v = (v * 10 + 1) % m;//遍历所有位数在1~n之间,形似1,11,111的数字 if(v==0 || lst[v]){//之前枚举过一个数字 % m = v,也就是说我们找到了循环(v=0说明循环节就是1) int sz=i-lst[v], tv=v;//sz就是循环节的长度 for(int j=0; j<sz; j++){//遍历整个循环节的模数 if((n-i-j) % sz == 0) v = tv;//剩下的数字个数是sz的整数倍,也就是说tv(v)就是最后一个数(n个1)%m的ans cnt[tv] = (cnt[tv] + (n>=i+j ? (n-i-j+sz)/sz : 0)) % P;//在一个循环节内每个模数只会出现一次 //统计之后有几个循环节就可以知道这一堆数中(1个1,2个1...)%m=x的有cnt[x]个 tv = (tv * 10 + 1) % m; } break; } cnt[v]++; lst[v] = i;//i个1 % m = v } for(int i=0; i<m; i++){// int t=1; for(int j=0;j<9;j++){ C[i][j] = t;//C[i][j]表示从这一堆数(1个1,2个1...)中找出j个%m=i的数有多少种方案 t = (LL)t * (cnt[i]-j) % P * inv[j+1] % P;//阶乘式子往下走 //printf("Y %d %d %d\n", i, j, [i][j]); } } u[0][0] = 1;//u[i][j]表示在i层中填充j种数的方案数,注意是填充而不是选j层出来,每个数一定要放进去。 //eg:u[3][2] = 2; 也就是说在3层中填充2种数的方案数为2,假设我们的2种数是1,2 //填充方式是1,1,2或者1,2,2。不计顺序(因为我们最终的ans是它们的和) //u[i][j] = C(i-1, j-1)(组合数) 夹棍法考虑,因为不计顺序,那么我们的任务就是把i层分成j段,每段一个数 //所以u的求法就有两种下面是根据意义的递推,也可以直接算组合数 for(int i=1; i<9; i++) for(int j=1; j<=i; j++){ for(int k=1; k<=i; k++) (u[i][j] += u[i-k][j-1]) %= P;//递推 //在i层中填充j种数的方案数=在i-k层中填充j-1种数的方案数(剩下的层填充最后一种)顺序并没有影响 //printf("Y %d %d %d\n", i, j, [i][j]); } for(int i=0; i<m; i++){ for(int j=0; j<9; j++){ for(int k=0; k<=j; k++) g[i][j] = (g[i][j] + (LL)u[j][k] * C[i][k]) % P; //g[i][j]表示从这一堆数(1个1,2个1...)中找出j个%m=i的数的方案数(一个数可以选择多次) //从这一堆数(1个1,2个1...)中找出个%m=i的数的方案数 有C[i][k] //把这k个数放到这j层中的方案数有u[j][k]种,所以就是这样递推 //printf("Y %d %d %d\n", i, j, g[i][j]); } } f[0][v] = 1;//f[i][j]表示放上i层,使得所得ans%m=j的方案数 for(int i=0; i<m; i++){ for(int j=7; j>=0; j--){//类似背包 for(int k=j+1; k<9; k++){ for(int l=0; l<m; l++){ int &t = f[k][(l+i*(k-j))%m]; t = (t + (LL)f[j][l] * g[i][k-j]) % P;//选择k-j层放上%m=i的数,ans%m就=l+i*(k-j) } } } } int ans = 0; for(int i=0; i<9; i++) (ans += f[i][0]) %= P; printf("%d\n", ans);}
阅读全文
1 0
- 统计 (dp 求n位无前导零且数码从高到低非降并模 m等于 0的数个数 )
- Java中从m个数中随机选取n个不同的数(m>=n),并进行排序
- (c++)从键盘输入任意20个整型数,统计其中的负数个数并求所有正数的平均值。 保留两位小数
- 数位dp无前导零
- 求n个数选m个数的组合数
- HDU 2824 欧拉函数 求小于等于n且与n互质数的个数
- (hdu step 2.3.7)下沙的沙子有几粒?(简单DP:求有m个H,n和D,且任意索引上H的个数都要比D的个数多的方案数)
- [算法] 求排列组合: 从n个数中任选m个数组成一个新数
- 从M个数中选择前N大的数
- 求小于等于n的质数个数
- 从n个数挑k个,和等于m
- 2} 求从m到n的映射的个数 0 c} n{-2 b 设m={a
- E - A Magic LampHDU 3183(在删除指定个数的数后的到最小的数,且去前导0)
- 求M个数中前N个最大数(M>N)
- 输入二个数n ,m 求 m内所有之和等于n
- 背包问题:求小于M并且加起来和等于N的数
- 算法设计:有n个数,范围是从1到n,且只有唯一的两个数相同,如何最快的求相同的这个数值?
- 欧拉函数的延伸:关于求小于等于n且与n互质的数的和。
- tableViewCell设置底部线长度与屏幕等宽(备忘)
- hdu2966 In case of failure(这道题就和ta的name一样,failure)
- Datatable回到当前页
- Web应用的目录结构
- 图形与图像编程(五)-图像控制
- 统计 (dp 求n位无前导零且数码从高到低非降并模 m等于 0的数个数 )
- iOS巅峰之真机和模拟器下在沙盒存储数据NSSearchPathForDirectoriesInDomains与NSHomeDirectory的差异
- kafka性能参数和压力测试揭秘
- C#开发常见的异常
- iframe-页面展示PDF
- 刘晓庆8段感情都传奇,当过小三为姜文掏心掏肺现得富商老公进军微电商!
- 深度优先搜索DFS和广度优先搜索BFS的总结
- 自己实现阻塞队列
- 数据结构中常用排序