第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; }
标签:输出,int,res,矩阵,.....,序列,dp From: https://www.cnblogs.com/didiao233/p/18365988