幸运数(dp+矩阵加速)

第1题     幸运数 查看测评数据信息

小明认为只有数字4和7是幸运数字,其他数字都不是。

如果一个整数的每个数字都是幸运数字,那么该整数就是幸运整数。

给出一个数组numbers[1...n]。

一个长度为L的幸运数列A[0],A[1],A[2],...A[L-1]必须同时满足:

1、对于0<=i<L,       A[i]必须是幸运整数。

2、对于0<=i<L-1,A[i]的最后一个数字必须等于A[i+1]的第一个数字。

3、 对于0<=i<L,     至少存在一个下标j满足A[i] = numbers[j]。

输出有多少个不同的长度为L的幸运序列,答案模1234567891。

提示:对于两个长度都是L的幸运序列A[0], A[1], ..., A[L- 1] 和 B[0], B[1], ..., B[L - 1],他们被认为是不同序列的条件是:存在一个下标i, 使得A[i] != B[i] 

输入格式

 

第一行,两个整数,n和L。 1<=n<=50,  1<=L<=1e9。

第二行,n个整数,第i个整数是numbers[i], 1<=numbers[i]<=1e9。

 

输出格式

 

一个整数

 

输入/输出例子1

输入:

3 3

47 74 47 

 

输出:

2

 

输入/输出例子2

输入:

5 47

100 4774 200 747 300

 

输出:

2

 

样例解释

 

 

首先,根据题意,我们先初始化,后面方便dp

1.筛除不含4,7的数
2.去重

3.分类-4类
1) 4开头4结尾
2) 4开头7结尾
3) 7开头4结尾
4) 7开头7结尾

 

然后就很容易定出dp了

f(i, j): 当前已经构造的长度是i,并且第i个数是第j类

 

假设

cnt1表示 有多少个第一类
cnt2表示有多少个第二类

cnt3,cnt4以此类推

 

那么可以推出转移方程
f(L, 1)=( f(L-1, 1)+f(L-1, 3) ) * cnt1
f(L, 2)=( f(L-1, 1)+f(L-1, 3) ) * cnt2
f(L, 3)=( f(L-1, 2)+f(L-1, 4) ) * cnt3
f(L, 4)=( f(L-1, 2)+f(L-1, 4) ) * cnt4

 

举个例:

f(L, 3)表示长度为L,第L个数是第3类的(7开头,4结尾)

那么 L 肯定由 L-1 转移转移过来的。由于第L个数是7开头的,那么L-1个数肯定是7结尾的,然后我们考虑第L这一位,它可以从7开头7结尾的数中任选一个,也就是要乘上cnt3

剩余的同上。

 

推完dp后,可以开始矩阵加速了

 

由于平常的矩阵都是一维的,这里却是二维,不好算
我们考虑降维,我们把第一维拆分成阶段,(矩阵不断递推,不就算作为阶段么)第二维成为矩阵中的内容

 

[f1, f2, f3, f4]
假设当前已构造的长度是L


f1:长度是L的第1类的方案数
f2:长度是L的第2类的方案数
f3:长度是L的第3类的方案数
f4:长度是L的第4类的方案数

 

目标矩阵长度就是L+1
[f1', f2', f3', f4']
f1':长度是L+1的第1类的方案数
......

 

f1' = cnt1*f1+ cnt1*f3
f2' = cnt2*f1+ cnt2*f3

......(跟dp一个样,没啥好补充的了)

 

[f1, f2, f3, f4] * [B] = [f1', f2', f3', f4']

[B]:(根据上面的式子即可求出)

[cnt1,  cnt2,  0,       0 ]
[0,      0,       cnt3,   cnt4 ]
[cnt1, cnt2, 0,         0]
[0,      0,      cnt3,     cnt4]


答案:
[f1, f2, f3, f4]*[B]^(L-1)

ans=f1+f2+f3+f4

 

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=55, Mod=1234567891;

struct mp
{
	int n, m;
	int a[N][N];
	
	void init(int row, int col, bool isI)
	{
		n=row, m=col;
		memset(a, 0, sizeof a);
		
		if (isI)
			for (int i=1; i<=row; i++) a[i][i]=1;
	}	
}A, B;
mp operator * (const mp A, const mp B)
{
	mp C;
	C.init(A.n, B.m, 0);
	for (int i=1; i<=A.n; i++)
		for (int j=1; j<=B.m; j++)
			for (int k=1; k<=A.m; k++)
				C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%Mod;
		
	return C;
}
mp qpow_mp(mp A, int k)
{
	mp res;
	res.init(A.n, A.m, 1);
	while (k>0)
	{
		if (k&1) res=res*A;
		A=A*A;
		k>>=1;
	}
	
	return res;
}
int n, m, L, x, a[N], cnt[N];
signed main()
{
	scanf("%lld%lld", &n, &L);
	for (int i=1; i<=n; i++)
	{
		int t=0;
		bool ok=1;
		scanf("%lld", &x);
		
		t=x;
		while (t>0)
		{
			if (t%10!=4 && t%10!=7)
			{
				ok=0;
				break;
			}
			t/=10;
		}
		
		if (ok) a[++m]=x;
	} 
//	printf("{%d}", m);
	sort(a+1, a+1+m);
	m=unique(a+1, a+1+m)-a-1;
	
//	printf("{%d}", m);
	for (int i=1; i<=m; i++)
	{
		int t=a[i], fst=0, lst=0;
		
		while (t>0)
		{
			if (fst==0) fst=t%10;
			lst=t%10;
			t/=10;
		}
		
		if (fst==4 && lst==4) cnt[1]++;
		if (fst==4 && lst==7) cnt[2]++;
		if (fst==7 && lst==4) cnt[3]++;
		if (fst==7 && lst==7) cnt[4]++;
	}
	
	A.init(1, 4, 0);
	A.a[1][1]=cnt[1], A.a[1][2]=cnt[2], A.a[1][3]=cnt[3], A.a[1][4]=cnt[4];
	
	B.init(4, 4, 0);
	B.a[1][1]=cnt[1], B.a[1][2]=cnt[2], B.a[1][3]=0,      B.a[1][4]=0;
	B.a[2][1]=0,      B.a[2][2]=0,      B.a[2][3]=cnt[3], B.a[2][4]=cnt[4];
	B.a[3][1]=cnt[1], B.a[3][2]=cnt[2], B.a[3][3]=0,      B.a[3][4]=0;
	B.a[4][1]=0,      B.a[4][2]=0,      B.a[4][3]=cnt[3], B.a[4][4]=cnt[4];
	
	A=A*qpow_mp(B, L-1);
	
	printf("%lld", (((A.a[1][1]+A.a[1][2])%Mod+A.a[1][3])%Mod+A.a[1][4])%Mod);
	return 0;
}

  

 

 

 

posted @ 2024-08-18 19:48  cn是大帅哥886  阅读(91)  评论(0)    收藏  举报