標籤:blog http io ar os sp for on div
題目連結:BZOJ - 1875
題目分析:
這道題如果去掉“不會立刻沿著剛剛走來的路走回”的限制,直接用鄰接矩陣跑矩陣乘法就可以了。然而現在加了這個限制,建圖的方式就要做一些改變。如果我們把每一條邊看做點建矩陣,那麼每次從一條邊出發都只會到其他的邊,不能仍然在這條邊上“停留”,所以這就可以滿足題目的限制。將每條邊拆成兩條單向邊,比如一條編號為 4,一條編號為 5。那麼 4^1=5, 5^1=4。這樣只要不從第 i 條邊走到 i 或 i^1 就可以了。初始的矩陣中以 A 為起點的邊到達的方案數為 1 ,其餘為 0。最後將終點為 B 的邊的方案數累加即為答案。、
這種將邊與點靈活轉化的思想十分巧妙,應注意。
代碼如下:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <algorithm>using namespace std;const int MaxN = 20 + 5, MaxM = 120 + 5, Mod = 45989;int n, m, t, A, B, TopA, TopB, Index, RT, Ans, a, b;int EA[MaxM], EB[MaxM];struct Edge{int u, v;Edge() {}Edge(int a, int b) {u = a; v = b;}} E[MaxM];struct Matrix {int x, y, Num[MaxM][MaxM];void SetXY(int xx, int yy) {x = xx; y = yy;}void Clear(int nn) {for (int i = 0; i < x; ++i) {for (int j = 0; j < y; ++j) {Num[i][j] = nn;}}}} M0, MZ;Matrix Mul(Matrix A, Matrix B) {Matrix ret;ret.SetXY(A.x, B.y);ret.Clear(0);for (int i = 0; i < ret.x; ++i) {for (int j = 0; j < ret.y; ++j) {for (int k = 0; k < A.y; ++k) {ret.Num[i][j] += A.Num[i][k] * B.Num[k][j];ret.Num[i][j] %= Mod;}}}return ret;}Matrix Pow(Matrix A, int b) {Matrix ret, f;f = A;ret.SetXY(f.x, f.y);for (int i = 0; i <= ret.x; ++i) ret.Num[i][i] = 1;while (b) {if (b & 1) ret = Mul(ret, f);b >>= 1;f = Mul(f, f);}return ret;}int main() {scanf("%d%d%d%d%d", &n, &m, &t, &A, &B);Index = -1;for (int i = 1; i <= m; ++i) {scanf("%d%d", &a, &b);E[++Index] = Edge(a, b);E[++Index] = Edge(b, a);}MZ.SetXY(m * 2, m * 2);MZ.Clear(0);TopA = TopB = 0;for (int i = 0; i <= Index; ++i) {if (E[i].u == A) EA[++TopA] = i;if (E[i].v == B) EB[++TopB] = i;for (int j = 0; j <= Index; ++j) {if (i != j && i != (j ^ 1) && E[i].v == E[j].u) MZ.Num[i][j] = 1;}}M0.SetXY(1, m * 2); M0.Clear(0);for (int i = 1; i <= TopA; ++i) M0.Num[0][EA[i]] = 1;MZ = Pow(MZ, t - 1);M0 = Mul(M0, MZ);Ans = 0;for (int i = 1; i <= TopB; ++i) {Ans += M0.Num[0][EB[i]];Ans %= Mod;}printf("%d\n", Ans);return 0;}
[BZOJ 1875] [SDOI 2009] HH去散步【矩陣乘法】