【[Offer收割]编程练习赛23 C】【矩阵快速幂】H国的身份证号码II

来源:互联网 发布:怎么查淘宝授权书 编辑:程序博客网 时间:2024/05/22 09:06

题目3 : H国的身份证号码II

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

H国的身份证号码是一个N位的正整数(首位不能是0)。此外,由于防伪需要,一个N位正整数是合法的身份证号码当且仅当每位数字都小于等于K,并且任意相邻两位数字的乘积也小于等于K。

例如对于K=5, 101、211、210等都是合法的号码,而106、123、421等都是非法的号码。

给定一个正整数N以及K,H国总统想知道一共有多少个合法的号码可用。

输入

两个整数N和K。

对于30%的数据,1 ≤ N ≤ 10  

对于50%的数据,1 ≤ N ≤ 1000000

对于100%的数据,1 ≤ N ≤ 1012,1 ≤ K ≤ 81。

输出

合法号码的总数。由于答案可能非常大,你只需要输出答案对109+7取模的结果。

样例输入
2 4
样例输出
12

#include<stdio.h>#include<iostream>#include<string.h>#include<string>#include<ctype.h>#include<math.h>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }#define MS(x, y) memset(x, y, sizeof(x))#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }int casenum, casei;LL n; int K;vector<int>vt;void dfs(int p, int pre, int now){if (p > n){vt.push_back(now);return;}for (int i = 0; i <= K; ++i){if (i * pre > K)break;dfs(p + 1, i, now * 10 + i);}}int BF(){vt.clear();for (int i = 1; i <= K; ++i){dfs(2, i, i);}return vt.size();}const int G = 10;//矩阵大小struct MX{int v[G][G];void O() { MS(v, 0); }void E() { MS(v, 0); for (int i = 0; i < G; ++i)v[i][i] = 1; }void P(){for (int i = 0; i < G; ++i){for (int j = 0; j < G; ++j)printf("%d ", v[i][j]); puts("");}}MX operator * (const MX &b) const{MX c; c.O();for (int k = 0; k < G; ++k){for (int i = 0; i < G; ++i) if (v[i][k]){for (int j = 0; j < G; ++j){c.v[i][j] = (c.v[i][j] + (LL)v[i][k] * b.v[k][j]) % Z;}}}return c;}MX operator + (const MX &b) const{MX c; c.O();for (int i = 0; i < G; ++i){for (int j = 0; j < G; ++j){c.v[i][j] = (v[i][j] + b.v[i][j]) % Z;}}return c;}MX operator ^ (LL p) const{MX y; y.E();MX x; memcpy(x.v, v, sizeof(v));while (p){if (p & 1)y = y * x;x = x * x;p >>= 1;}return y;}}a, b, c;int main(){while (~scanf("%lld%d", &n, &K)){int top = min(9, K);b.O();for (int i = 0; i <= top; ++i){for (int j = 0; j <= top; ++j)if (i * j <= K){b.v[i][j] = 1;}}a.O();for (int i = 1; i <= top; ++i){a.v[0][i] = 1;}c = a * (b ^ (n - 1));int ans = 0;for (int i = 0; i <= top; ++i){gadd(ans, c.v[0][i]);}printf("%d\n", ans);//printf("%d\n", BF());}return 0;}/*【trick&&吐槽】变量写歪了wa了一发2333【题意】让你判定,对于首位不能为0的恰好n(n <= 10^12)位数,如果其每一位的数要<=K (K <= 81)且相邻位的数之乘积<=K,要你依次输出所有这样的数。【分析】这道题我们看到n实在是有够大,于是想到是矩阵快速幂。初始化和转移都很简单。【时间复杂度&&优化】O(log(n) * 10^3)*/