题意:给出一个无向图,有一个bean开始在1号节点上。每一秒中它可以跳到相接的点上,或者停在原处,它时刻都可能爆炸,问在t秒内,它有多少种行为数目。
思路:dp,定义dp[i][j][1]表示i秒的时刻停在j上,并且没有爆炸的方案数目。dp[i][j][0]表示i秒停在j上的方案数目(包括爆炸了)。
状态转移方程为:dp[i][j][0] = dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][k][1], dp[i][j][1] = dp[i-1][j][1]+dp[i-1][k][1](k表示与j相邻的点),由于这里t<10^6,所以必须用矩阵快速幂进行加速。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define mod 2007
#define N 42
inline void add(int &i, int j) {
i += j;
if (i >= mod) i -= mod;
}
int n;
struct M{
int v[N][N];
M operator*(M a) const{
int i, j, k;
M tm;
memset(tm.v, 0, sizeof(tm.v));
int m = (n<<1);
for (i = 0;i < m;i++) {
for (j = 0;j < m;j++) {
for (k = 0;k < m;k++)
add(tm.v[i][j], v[i][k]*a.v[k][j]%mod);
}
}
return tm;
}
}E, A;
bool vis[N][N];
int dp[N];
M cal(M a, int b) {
M tm = E;
while (b) {
if (b&1) tm = tm*a;
a = a*a, b>>=1;
}
return tm;
}
void init() {
int i, j, l, y;
memset(A.v, 0, sizeof(A.v));
memset(dp, 0, sizeof(dp));
for (i = 1;i <= n;i++) {
l = i*2-2, y = l+1;
A.v[l][l] = A.v[y][l] = 1;
A.v[y][y] = 1;
for (j = 1;j <= n;j++) {
if (!vis[i][j]) continue;
A.v[2*j-1][l] = A.v[2*j-1][y] = 1;
}
}
dp[0] = dp[1] = 1;
}
int main() {
int m, i, j, u, v, t, k;
for (i = 0;i < N;i++) E.v[i][i] = 1;
while (scanf("%d%d", &n, &m), n||m) {
memset(vis, false, sizeof(vis));
for (i = 0;i < m;i++) {
scanf("%d%d", &u, &v);
if (vis[u][v]) continue;
vis[u][v] = vis[v][u] = true;
}
init();
scanf("%d", &t);
A = cal(A, t);
int tm, ans = 0;
for (i = 1;i <= n;i++) {
tm = 0;
for (k = 0;k < 2*n;k++) {
add(tm, dp[k]*A.v[k][2*i-2]%mod);
}
add(ans, tm);
}
printf("%d\n", ans);
}
}