序列(dp+矩阵加速)

第2题     序列 查看测评数据信息

给定一个数集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;
}

  

 

 

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