1 3 2 1 2 1 3 0
6Hintpossible patterns are ∅, 1, 2, 3, 1→2, 2→3
建立转移矩阵,增加一个结点,表示ans的情况即可。
===========================
根据有人评论,如何记录ans的数量的。我们现在假设以下方程A是转移矩阵,有三种状态
如果是线性的转移,那么每一次转移,ans += dp[1]+dp[2]+dp[3],这样就记录了当前长度的答案了。
如何在矩阵中实现到ans的转移呢?
在矩阵中加入ans这个状态,由于刚开始,长度为0的种类有一个,构造如下矩阵与状态A表示转移矩阵。
A[1,1,1,1] = A[dp[1],dp[2],dp[4],ans] ( 长度为1的情况下,任何数都可以放,所以都是1)
接下来是如何构造A使得每次乘上A,都会使得ans更新:
如下 A =
x x x 0 dp[1]
x x x 0 * dp[2]
x x x 0 dp[3]
1 1 1 1 ans
那么可知每次乘A,dp值都会变,dp值表示长度为当前长度,以i结尾的情况数。
那么在这次乘法中 ans = dp[1] + dp[2] + dp[3] + ans , ans不仅记录了上一次的值,并且还加上了上一次的
dp[1],dp[2],dp[3]的值,也就是说,ans是不同长度的情况下的累加和。
那么用矩阵加速也就能理解。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; int mod = 2015; const int maxn = 52; struct Node{ int mat[maxn][maxn]; Node(){ memset(mat,0,sizeof(mat)); } }; Node operator*(Node a,Node b){ Node c; for(int i = 0;i < maxn; i++) for(int j = 0;j < maxn; j++) for(int k = 0;k < maxn; k++) c.mat[i][j] += a.mat[i][k]*b.mat[k][j]; for(int i = 0;i < maxn; i++) for(int j = 0;j < maxn; j++) c.mat[i][j] %= mod; return c; } int main(){ int t,n,m; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); Node a,b,c; for(int i = 0;i < n; i++){ int k; scanf("%d",&k); while(k--){ int u; scanf("%d",&u); u--; a.mat[u][i] = 1; } } for(int i = 0;i <= n; i++) a.mat[n][i] = 1; for(int i = 0;i <= n; i++) b.mat[i][i] = 1; int ans = 0; while(m){ if(m&1) b = a*b; a = a*a; m/=2; } for(int i = 0;i <= n; i++) ans += b.mat[n][i]; ans %= mod; printf("%d\n",ans); } return 0; }