hdu 5564 && bestcode 62 Clarke and digits

題目:

Clarke and digits

Accepts: 16
Submissions: 29
Time Limit: 5000/3000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
問題描述
克拉克是一名人格分裂患者。某一天,克拉克變成了一個研究人員,在研究數字。  
他想知道在所有長度在[l,r][l, r][l,r]之間的能被777整除且相鄰數位之和不爲kkk的正整數有多少個。  
輸入描述
第一行一個整數T(1≤T≤5)T(1 \le T \le 5)T(1T5),表示數據的組數。  
每組數據只有一行三個整數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(1lr109,0k18)
輸出描述
每組數據輸出一行一個數,表示答案。由於答案太大,你只需對109+710^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,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)表示長度爲iiiiii位爲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,(k10+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);*/
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章