HDU 5564:Clarke and digits 收获颇多的矩阵快速幂 + 前缀和
2015-11-19 23:40
316 查看
Clarke and digits
Accepts: 16Submissions: 29
Time Limit: 5000/3000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
克拉克是一名人格分裂患者。某一天,克拉克变成了一个研究人员,在研究数字。 他想知道在所有长度在[l, r][l,r]之间的能被77整除且相邻数位之和不为kk的正整数有多少个。
输入描述
第一行一个整数T(1 \le T \le 5)T(1≤T≤5),表示数据的组数。 每组数据只有一行三个整数l, r, k(1 \le l \le r \le 10^9, 0 \le k \le 18)l,r,k(1≤l≤r≤109,0≤k≤18)。
输出描述
每组数据输出一行一个数,表示答案。由于答案太大,你只需对10^9+7109+7取模即可。
输入样例
2 1 2 5 2 3 5
输出样例
13 125
Hint
第一个样例有13个数满足,分别是:7,21,28,35,42,49,56,63,70,77,84,91,987,21,28,35,42,49,56,63,70,77,84,91,98
这个题目看着题解想了一天,收获特别多多多多。
一开始刚看这个题目的时候想法是dp, dp[i][j][k]表示长度为i,最后一位为j,余数为k的个数。
然后转移方程就是 dp[i+1][x][(j*10+x)%7] += dp[i][j][k] (x从0到9)。这样不行因为R太大,每次求一定TLE。我当前的水平就想到这里了。。。
然后就是学习了。。。
学习点之一:发现当前最后一位j与当前的余数k,其状态数就70个。有趣的是,其状态转移是确定的,即是一个70*70的矩阵,这样我们对长度为X的数,我们先求长度为1的矩阵A,再将A*转移方程B^(X-1)。然后就是矩阵中的[0][0^9]的和。(表示余数为0,末尾数字是0~9)。然后矩阵的多少次方就可以用快速幂来求。
学习点之二:注意这样求的是长度为X的数。我们现在想求的是长度为1到长度为X的数,这样对于[L,R]来说,直接相减就好了。所以就想求A、A*B、A*B^2、A*B^3、。。。这样的前缀和。
之前求矩阵的前缀和求过这样的B、B^2、B^3、B^4。。。。可以用分治法来求。
但这样的我就不知道怎么搞了。
结果题解上是加了一列matrix[0^9][70]=1,并且matrix[70][70]=1。
这样当我们求A*(B^R)的时候,res.matrix[0][70]表示的就是前缀和矩阵的所有和。这块做法太亮了,太亮了。之前没有玩过这种方法。
附加学习点之三:friend Matrix operator ^(Matrix A, int n);这块调用了A矩阵,其值发生了变化,每一个test需要重置一下B矩阵。
最后想通的感觉特别痛快。
代码:
#include <iostream> #include <algorithm> #include <cmath> #include <vector> #include <string> #include <cstring> #pragma warning(disable:4996) using namespace std; const int maxn = 71; const int mod = 1e9 + 7; struct Matrix { int mat[maxn][maxn]; Matrix() { memset(mat, 0, sizeof(mat)); } void clear(){ memset(mat, 0, sizeof(mat)); } friend Matrix operator *(const Matrix &A, const Matrix &B); friend Matrix operator ^(Matrix A, int n); }; Matrix res1, res2; Matrix operator *(const Matrix &A, const Matrix &B) { Matrix ret; for (int i = 0; i < maxn; i++) { for (int j = 0; j < maxn; j++) { for (int k = 0; k < maxn; k++) { ret.mat[i][j] = (ret.mat[i][j] + (1LL*A.mat[i][k] * B.mat[k][j]) % mod) % mod; } } } return ret; } Matrix operator ^(Matrix A, int n) { Matrix ret; for (int i = 0; i < maxn; i++) { ret.mat[i][i] = 1; } for (; n; n >>= 1, A = A*A) if (n & 1) ret = ret*A; return ret; } inline int statu(int i, int j) { return i * 10 + j; } int main() { //freopen("i.txt","r",stdin); //freopen("o.txt","w",stdout); int t; int i, j, L, R, K, x; Matrix A, B; for (i = 1; i < 10; i++) A.mat[0][statu(i % 7, i)] = 1; scanf("%d", &t); while (t--) { scanf("%d%d%d", &L, &R, &K); B.clear(); for (i = 0; i < 7; i++) { for (j = 0; j < 10; j++) { for (x = 0; x < 10; x++) { if (j + x != K) { B.mat[statu(i, j)][statu((i * 10 + x) % 7, x)] = 1; } } } } for (i = 0; i < 10; i++) B.mat[i][maxn - 1] = 1; B.mat[maxn - 1][maxn - 1] = 1; res1 = A*(B^R); res2 = A * (B ^ (L - 1)); printf("%d\n", (res1.mat[0][maxn - 1] - res2.mat[0][maxn - 1] + mod) % mod); } //system("pause"); return 0; }
相关文章推荐
- html5中datalist简单用法
- strstr查找子字符串函数
- 【C语言】【面试题】【笔试题】编写一个函数实现n^k,使用递归实现
- UINavigationController
- ios之performSelector 那点事
- POJ 2001 Shortest Prefixes
- phonegap android 开发环境搭建
- sourcetree的使用
- Linux——磁盘及文件系统
- 决策树归纳分类算法理解
- grep命令---Linux学习笔记
- 【C语言】在终端输入多行信息,找出包含“ould”的行,并打印改行
- 南大软院大神养成计划-学习感悟
- 文件系统缓存dirty_ratio与dirty_background_ratio两个参数区别
- VC++ 与C 语言的区别
- 比较不错的几款开源的WPF Charts报表控件
- UIView,UIButton,UIImageView等设置圆角,设置阴影,设置边框的方法
- MongoDB——牛X的索引操作
- EF5批量操作数据库(批量创建、删除、附加、分离、脱机、联机)
- 二维码扫描