題目:
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);*/
}