行为方案 / 可乐 / 可乐(数据加强版)
题目链接:ybt高效进阶6-1-3 / luogu P3758 / luogu P5789
题目大意
给你一个图,一开始在 1 号点,然后对于每个时间单位,你可以结束行走,或留在原地,或前往一个连着的地方。
然后问你经过了 t 个时间单位,你行为的方案数是多少。
思路
考虑 DP,设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k 为从
i
i
i 走到
j
j
j,过了
k
k
k 个时间的方案数。
然后能停留就是一开始
f
i
,
i
,
1
=
1
f_{i,i,1}=1
fi,i,1=1,然后对于爆炸我们就开一个
0
0
0 点,走到了就代表爆炸了
f
i
,
0
,
1
=
1
f_{i,0,1}=1
fi,0,1=1。
然后转移就是
f
i
,
j
,
k
=
∑
f
i
,
l
,
k
−
1
×
f
l
,
j
,
1
f_{i,j,k}=\sum f_{i,l,k-1}\times f_{l,j,1}
fi,j,k=∑fi,l,k−1×fl,j,1
然后考虑优化,发现时间空间都过不去,考虑一个一个搞。
发现第三维只跟前面一个和第一个有关,就可以把第三维去掉,空间过了。
然后再看式子,你会发现它很像矩阵乘法,然后你就用矩阵快速幂加速,那时间也可以了。
代码
#include<cstdio>
#include<cstring>
#define mo 2017
using namespace std;
struct matrix {
int n, m;
int a[101][101];
}a;
int n, m, t, ans, x, y;
matrix operator *(matrix x, matrix y) {
matrix z;
z.n = x.n; z.m = y.m;
memset(z.a, 0, sizeof(z.a));
for (int k = 0; k < x.m; k++)
for (int i = 0; i < z.n; i++)
for (int j = 0; j < z.m; j++)
z.a[i][j] = (z.a[i][j] + (x.a[i][k] * y.a[k][j]) % mo) % mo;
return z;
}
matrix one(int n) {
matrix re;
re.n = re.m = n;
memset(re.a, 0, sizeof(re.a));
for (int i = 0; i < re.n; i++)
re.a[i][i] = 1;
return re;
}
matrix jzksm(matrix x, int y) {
matrix re = one(x.n);
while (y) {
if (y & 1) re = re * x;
x = x * x;
y >>= 1;
}
return re;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d %d", &x, &y);
a.a[x][y] = 1; a.a[y][x] = 1;
}
a.n = a.m = n + 1;
for (int i = 0; i <= n; i++) {//炸了之后就是一直留在原地,所以要枚举 0
a.a[i][i] = 1; a.a[i][0] = 1;//可能留在原地,也可能炸了
}
scanf("%d", &t);
a = jzksm(a, t);
for (int i = 0; i <= n; i++) ans = (ans + a.a[1][i]) % mo;
printf("%d", ans);
return 0;
}