給定一個數集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; }