link
題意:
小 T 迷失在了一個有 n 個點的群島上。
初始時他在 1 號島,他要通過架在島間的 m 座雙向橋,在正好過 k 座橋時達到 n 號島的大門。
這些橋中有若干座附魔橋。當小 T 經過一座附魔橋時,如果他身上沒有附魔標記則被標記,如果他身上已有附魔標記則標記消失。
大門只會在他身上有附魔標記時才會開啟,只有這樣他才能逃離。
小 T 迷失在了群島之間,他每次會等概率隨機挑選一座與他所在島嶼相連的橋走。小 T 向你詢問他能逃離的概率。
保證圖無自環無重邊。
分析:
用 \(A [ s ] [ i ] [ j ] [ w ]\)表示從\(i\)島恰好經過 \(2^s\)條邊到達 \(j\)島且狀態為 \(w\)時的概率。
題目中 \(k≤10^6\),所以 \(s\)最大不超過20,從低到高枚舉 \(s\),並利用Floyd更新 \(A\)即可。最后從\(A\)中湊出 \(k\)條邊的值即可。
用背包二進制優化即可將步數優化掉\(k=1e^6\)然后每次維護是否是附魔狀態。
或者直接利用快速冪來更新 \(A\)的同時,求出答案。
ll qpow(ll a, ll b)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
int n;
ll A[22][110][110][2];
ll g[110][110][2];
ll tmp[110][110][2];
int e[110][110];
void update(int f, int s) ///背包二進制優化
{
for(int k = 1; k <= n; k++)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
A[s][i][j][0] += A[f][i][k][0] * A[f][k][j][0] % mod; ///2^f步都沒有附魔
A[s][i][j][0] += A[f][i][k][1] * A[f][k][j][1] % mod; ///都附魔了
A[s][i][j][1] += A[f][i][k][0] * A[f][k][j][1] % mod;
A[s][i][j][1] += A[f][i][k][1] * A[f][k][j][0] % mod;
A[s][i][j][0] %= mod; //都要取模
A[s][i][j][1] %= mod;
}
}
}
}
void add(int f)
{
memset(tmp, 0, sizeof tmp);
for(int k = 1; k <= n; k++)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
tmp[i][j][0] += g[i][k][0] * A[f][k][j][0] % mod;
tmp[i][j][0] += g[i][k][1] * A[f][k][j][1] % mod;
tmp[i][j][1] += g[i][k][1] * A[f][k][j][0] % mod;
tmp[i][j][1] += g[i][k][0] * A[f][k][j][1] % mod;
tmp[i][j][0] %= mod;
tmp[i][j][1] %= mod;
}
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 0; k <= 1; k++)
{
g[i][j][k] = tmp[i][j][k];
}
}
}
}
void solve()
{
int m, k;
scanf("%d%d%d", &n, &m, &k);
memset(e, -1, sizeof e);
memset(A, 0, sizeof A);
memset(g, 0, sizeof g);
for(int i = 1; i <= m; i++)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
e[x][y] = z; ///建橋
e[y][x] = z; //是否是附魔橋
}
for(int i = 1; i <= n; i++)
{
int sum = 0;
for(int j = 1; j <= n; j++)
{
sum += (e[i][j] >= 0); ///是否存在這座橋
}
for(int j = 1; j <= n; j++)
{
if(e[i][j] < 0) continue; ///不存在這座橋
A[0][i][j][e[i][j]] = qpow(sum, mod - 2);
}
}
for(int i = 1; i <= n; i++) ///一步 初始化答案
{
for(int j = 1; j <= n; j++)
{
g[i][j][0] = A[0][i][j][0];
g[i][j][1] = A[0][i][j][1];
}
}
for(int i = 1; (1 << i) <= k; i++)
update(i - 1, i);
k--;
for(int i = 20; i >= 0; i--)
{
if(k >= (1 << i))
{
add(i);
k -= (1 << i);
}
}
printf("%lld\n", g[1][n][1]);
}