题目:
Clarke and digits
克拉克是一名人格分裂患者。某一天,克拉克变成了一个研究人员,在研究数字。 他想知道在所有长度在[l,r][l, r][l,r]之间的能被777整除且相邻数位之和不为kkk的正整数有多少个。
第一行一个整数T(1≤T≤5)T(1 \le T \le 5)T(1≤T≤5),表示数据的组数。 每组数据只有一行三个整数l,r,k(1≤l≤r≤109,0≤k≤18)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)。
每组数据输出一行一个数,表示答案。由于答案太大,你只需对109+710^9+7109+7取模即可。
2 1 2 5 2 3 5
13 125
第一个样例有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,987,21,28,35,42,49,56,63,70,77,84,91,98
官方题解:考虑dp,令d(i,j,k)d(i, j, k)d(i,j,k)表示长度为iii第iii位为jjj余数为kkk的方案数,则d(1,j,j mod 7)=1,0<j<10d(1, j, j \ mod \ 7) = 1, 0 < j < 10d(1,j,j mod 7)=1,0<j<10, d(i+1,x,(k∗10+x) mod 7)+=d(i,j,k)d(i+1, x, (k * 10+x) \ mod \ 7) += d(i, j, k)d(i+1,x,(k∗10+x) mod 7)+=d(i,j,k)。发现转移相同,所以我们用矩阵快速幂来计算即可。题目要求前缀和,那么我们在矩阵里加一维就行了。
之前2015多校有一个比这稍微简单矩阵快速幂,那题只要把暴力版写好三重循环,然后按照矩阵乘法进行改造,即把一个判断条件改为全是01的二维数组。然而本题再按照以往的思路就绕弯路,至少麻烦。其实就是之前方法不是矩阵快速幂通用解法。
回到本题,发现转移相同,我们把状态转移存放在二维数组vert中,每个状态由最后一位的值和当前的数mod7确定,我们发现可以压缩成一个二位数,下一个状态就是在当前的状态下向最低位填一个数(因为这么转移简单,如果向高位转移,会发现还与权值有关,位数特大,几乎不可能),初始化vert数组后,我们还需初始化只有一位数的所有状态数组init。因为数组里的数经过压缩(mod*10 + i),i代表最后一位,mod代表%7后的值,因此能被7整除的方案数就存放在init数组中的0行第0-9列,但其和只代表该位时的方案数,还需求前缀合。这里我是在init第0行存放前缀和,根据矩阵乘法规则,因此还需在vert数组最后一列第0-9行设置为1,代表加上该数位的合法方案数,还需vect数组最后一列,最后一行设置1,代表加上之前累加的结果。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
const int maxn =78;
const int Mod=1e9+7;
struct matri
{
long long a[maxn][maxn];
matri()
{
memset(a,0,sizeof(a));
}
// friend matri operator *(const matri & a,const matri &b);
// friend matri operator ^(matri & a,int len);
};
matri operator *(matri a1, matri b1)
{
int i,j,k;
matri tmp;
for(i=0;i<maxn;i++)
{
for(j=0;j<maxn;j++)
{
for(k=0;k<maxn;k++)
{
tmp.a[i][j]=(tmp.a[i][j]+a1.a[i][k] * b1.a[k][j])%Mod;
}
}
}
return tmp;
}
matri operator ^ (matri a1,int len)
{
int i,j,k;
matri tmp;
for(i=0;i<maxn;i++) tmp.a[i][i]=1;
for(;len>0;len/=2)
{
if(len%2==1)
tmp=tmp*a1;
a1=a1*a1;
}
return tmp;
}
int depress(int i,int mod)
{
return mod*10+i;
}
matri get_vert(int lim)
{
matri B;
int i,j,k,n,x,m,mod;
for(i=0;i<=9;i++)
{
for(mod=0;mod<7;mod++)
{
for(j=0;j<=9;j++)
{
x = (mod*10 + j)%7;
if(i+j!=lim)
{
B.a[depress(i,mod)][depress(j,x)]=1;
}
}
}
}
for(i=0;i<10;i++) //记录前缀和
{
B.a[i][maxn-1]=1;
}
B.a[maxn-1][maxn-1]=1;
return B;
}
matri get_init()
{
matri tmp;
int i,j;
for(i=1;i<=9;i++)
{
tmp.a[0][depress(i,i%7)]=1;
}
//tmp.a[0][maxn-1]=0;
return tmp;
}
void test(matri B);
long long f(int r,int k)
{
matri B,A,C,ans1;
B=get_vert(k);
C=B^(r);
A=get_init();
ans1 = A*C;
return ans1.a[0][maxn-1];
// (ans+=ans1.a[i][maxn-1])%Mod;
// return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int i,j,n,m,t,l,r,k;
//f(1,5);
scanf("%d",&t);
while(t--){
scanf("%d %d %d",&l,&r,&k);
printf("%I64d\n",(f(r,k)-f(l-1,k)+Mod)%Mod);
}
return 0;
}
void test(matri C)
{
int i,j,k;
// matri B,C;
// for(i=0;i<2;i++)
// for(j=0;j<2;j++)
// B.a[i][j]=j+1;
// C=B^9;
//for(i=0;i<maxn;i++)
{
for(j=0;j<10;j++)
{
printf("%d !",C.a[0][j]);
}
printf("s=%d\n",C.a[0][maxn-1]);
printf("\n");
}
/*B=get_vert(k);
A=get_init();
test(A);
A=A*B;
test(A);
A=A*B;
test(A);
A=A*B;
test(A);
A=A*B;
test(A);*/
}