#include<stdio.h> #include<string.h> #include<ctype.h> #include<math.h> #include<iostream> #include<string> #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 MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef 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 G=71;//矩阵大小 const int Z=1e9+7;//取模数 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;} MX operator * (const MX &b) const { MX c;c.O(); for(int i=0;i<G;i++) { for(int j=0;j<G;j++) { for(int k=0;k<G;k++) { c.v[i][j]=(c.v[i][j]+(LL)v[i][k]*b.v[k][j])%Z; } } } return c; } MX operator ^ (int p) const { MX y;y.E(); MX x;MC(x.v,v); while(p) { if(p&1)y=y*x; x=x*x; p>>=1; } return y; } }a,b,c; int casenum,casei; int l,r,K; int main() { a.O(); for(int i=1;i<10;i++) { int j=i%7; int o=j*10+i; a.v[0][o]=1; } scanf("%d",&casenum); for(casei=1;casei<=casenum;casei++) { scanf("%d%d%d",&l,&r,&K);//--l;--r; b.O(); for(int i=0;i<7;i++)//枚举余数 { for(int j=0;j<10;j++)//枚举尾数 { int o1=i*10+j; for(int u=0;u<7;u++)//枚举余数 { for(int v=0;v<10;v++)//枚举尾数 { int o2=u*10+v; b.v[o1][o2]=(j+v!=K&&(i*10+v)%7==u); } } } } for(int j=0;j<10;j++)b.v[j][70]=1; b.v[70][70]=1; c=a*(b^r);int ansr=c.v[0][70]; c=a*(b^(l-1));int ansl=c.v[0][70]; int ans=(ansr+Z-ansl)%Z; printf("%d\n",ans); } return 0; } /* 【trick&&吐槽】 不要一开始就思考解决问题, 可以先简化数据规模,从小数据入手,再优化,就能AC看起来比较难的题目啦! 【题意】 对于一个正整数,如果其恰好是7的倍数,而且任意相邻两位的数都不为K,那么我们称这个数为7-K number, 现在问你,长度在[l,r]之间的7-K number的个数是多少,输出其mod (1e9+7)的值。 其中,K∈[0,18],l<=r<=1e9 【类型】 DP转矩阵快速幂 【分析】 首先,[l,r]大的我们不敢DP。但是我们应该先想到DP的做法,再进而考虑优化与转移—— 怎么DP呢?要先考虑状态的设置—— 用f[i][j][k]表示长度为i的串,尾数是j,目前%7的余数为k的数串的个数。 我们考虑使得这个数串*10,然后再加上一个尾数。这种做法恰好可以避免出现前导0的情况。 初始情况下,有f[1][x][x%7]=1; x∈[1,9] 那么我们枚举下一位的尾数u,u∈[0,9]便有状态转移方程—— f[i+1][u][(k*10+u)%7]+=f[i][j][k],转移的条件是u+j≠K 最后f[l~r][0~9][0]就是答案 然而这个DP的时间复杂度是 长度(i)*10(j)*7(k)*10(u),长度太大,于是时间复杂度爆炸。 我们寻求的解决办法便是——矩阵快速幂。 (尾数,余数)的可能性一共也不过只有70种,我们列出一个70*70的矩阵b[][] 我们把(余数*10+尾数)定义为状态,那么b[i][j]=1则表示由状态i向状态j可以转移 这样我们求b^r和b^(l-1),a*(b^r)-a*(b^(l-1))就是答案啦。 ?!?! QwQ ?!?! 并不对!这个是错误的。 a*(b^r)对应的是长度恰好为r,b^(l-1)对应的是长度恰好为l-1。 正确的做法是怎样呢? 我们需要累加一个前缀和,于是引入第71维。 使得b[0~9][70]都为1,b[70][70]也为1. 这样a*(b^p),得到的是这个DP转移[0,p-1]次(即长度在[1,p]范围下)的合法数串的个数 于是这样,a*(b^r)-a*(b^(l-1))就是答案啦。 【时间复杂度&&优化】 O(T * 71^3 * (log(r)+log(l)) ),运算上限大概是7e7,可以在1s内AC~ */