序列(dp+矩陣加速)

cn是大帅哥886發表於2024-08-18

給定一個數集A,現在你需要構造一個長度為k的序列B,序列B的元素從數集A中任意挑選,要求B中任意相鄰的兩個數字的異或值二進位制表示中1的個數是3的倍數,

請問B的有多少種合法的構造方案?兩種方案不同當且僅當存在B[i]在A中的位置不同。

輸入格式

第一行兩個數字n和k,表示數集大小和序列B的長度, 接下來一行有n個數字,代表數集中的元素。

100%的資料:1≤n≤100,1≤k≤10^18,0≤a[i]≤10^18

輸出格式

輸出一行,表示答案,由於答案可能會很大,請對10^9+7取模後輸出。

輸入/輸出例子1

輸入:

5 2

15 1 2 4 8

輸出:

13

輸入/輸出例子2

輸入:

5 1

15 1 2 4 8

輸出:

5

樣例解釋

一樣,先考慮dp

f(i, j)表示:b序列構造出來了長度為i,第i個數放的是a[j]的構造方案數量

考慮從i-1的位置轉移

假設i-1的位置放了a[k],要滿足
cnt(a[k]^a[j]) %3 == 0

才能轉移:
f(i, j) += f(i-1, k)

從資料入手,看到n很小。可以預處理合法的a[i], a[j]

現在考慮矩陣加速,但是dp是兩維的。
第一維是階段,可以把第一維捨棄。

假設,當前已經構造了i-1的長度
f(1) : 放a[1]的方案數, f(2): 放a[2]的方案數 .....
b[i-1] : a[1], a[2]......

那麼現在要求i長度時f(1)'~f(n)'
f(1)' : 放a[1]的方案數, f(2)': 放a[2]的方案數 .....
b[i]=a[1], a[2].......
已知:
[ f(1), f(2), f(3) ......, f(n) ] * [] = [ f(1)', f(2)' ....., f(n)']

假設a[1]和a[2], a[3], a[5]合法。
那麼f(1)'可以由2,3,5轉移
矩陣就是
[0 .....
[1 .....
[1 ....
[0 ....
[1 .....


我們就可以填矩陣了,然後快速冪,就完事了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=105, Mod=1e9+7;


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, k, a[N], ans=0;
int count(int x)
{
	int res=0;
	while (x>0)
	{
		if (x&1) res++;
		x>>=1;
	} 
	
	return res;
}
signed main()
{
	scanf("%lld%lld", &n, &k);
	B.init(n, n, 0);
	
	for (int i=1; i<=n; i++) scanf("%lld", &a[i]);
	for (int i=1; i<=n; i++)
		for (int j=1; j<=n; j++)
			if (count(a[i]^a[j])%3==0) B.a[i][j]=1;
	
	A.init(1, n, 0);
	for (int i=1; i<=n; i++) A.a[1][i]=1;
	
	A=A*qpow_mp(B, k-1);
	
	for (int i=1; i<=n; i++) ans=(ans+A.a[1][i])%Mod;
	
	printf("%lld", ans);
	return 0;
}

  

相關文章