HDU 5411
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5411
题意:
n个点,能构成长度小于等于m的序列有多少种,给出每个数字后能接的另外一个数字。
思路:
o(n*n*m)的dp来了一发T的,才看的题解。
题解用矩阵快速幂做的,关键就是把原来的n*n关系矩阵矩阵添加成(n+1)*(n+1)的矩阵,第n+1列全设为1。然后运算矩阵快速幂,由于最后一列的存在会把之前矩阵前n列元素的和加到n+1列上。
证明如下:
设当前矩阵(A为原始关系矩阵,最右边一列全设为1。则下一个矩阵中A’ = A * A。第n+1行不动,由于最后一列都是1,根据矩阵运算的性质会把A中所有同行元素作和之后加到最后一列。证毕。
源码:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define MOD (2015)
LL data[55][55];
struct Matrix
{
LL mat[55][55];
int n, m;
Matrix(){memset(mat, 0, sizeof(mat)); n = m = 0;}
Matrix(LL a[55][55], int _n, int _m)
{
n = _n, m = _m;
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++)
mat[i][j] = a[i][j];
}
Matrix operator *(const Matrix &b)
{
Matrix ans;
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j++){
for(int k = 1; k <= b.m ; k++)
ans.mat[i][k] = (ans.mat[i][k] + mat[i][j] * b.mat[j][k] ) % 2015;
}
}
ans.m = b.m; ans.n = n;
return ans;
}
void operator =(const Matrix &b)
{
n = b.n, m = b.m;
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++)
mat[i][j] = b.mat[i][j];
}
void output()
{
printf("Matrix\n");
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j++)
printf("%I64d ", mat[i][j]);
printf("\n");
}
printf("Matrix\n");
}
LL UnitSum()
{
LL ans = 0;
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
ans = (ans + mat[i][j]) % MOD;
return ans;
}
};
Matrix ppow(Matrix a, int n)
{
Matrix ans;
for(int i = 1 ; i <= a.n ; i++)
ans.mat[i][i] = 1;
ans.n = a.n;
ans.m = a.m;
while(n)
{
// ans.output();
// printf("\n");
if(n & 1){
ans = ans * a;
}
a = a * a;
n = n >> 1;
}
return ans;
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
memset(data, 0, sizeof(data));
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1 ; i <= n ; i++){
int k;
scanf("%d", &k);
for(int j = 0 ; j < k ; j++){
int temp;
scanf("%d", &temp);
data[i][temp] = 1;
}
data[i][n + 1] = 1;
// data[i][i] = 1;
}
data[n + 1][n + 1] = 1;
Matrix ans = Matrix(data, n + 1, n + 1);
if( m >= 1){
ans = ppow(ans, m - 1);
printf("%I64d\n", (ans.UnitSum()) % 2015);
}
else{
printf("1\n");
}
}
return 0;
}
/*
100
3 1
1 2
1 3
0
*/