幸運數(dp+矩陣加速)

cn是大帅哥886發表於2024-08-18
第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

樣例解釋

首先,根據題意,我們先初始化,後面方便d

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;
}

  

相關文章