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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值