Luogu 2151 [SDOI2009]HH去散步

BZOJ 1875

矩阵乘法加速递推。

如果不要求不能走同一条边,那么直接构造出矩阵快速幂即可,但是不走相同的道路,怎么办?

发现边数$m$也很小,我们直接把$2 * m$开成一个矩阵,相当于记录上一条边走过了编号为$j$的边的方案总数,这样子我们在构造转移矩阵的时候就可以不用计算往回走带来的贡献了。

时间复杂度$O(m^3log(MaxInt))$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 55;
const int M = 125;
const int P = 45989;

int n, m, tot = 0, from[M], to[M];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

template <typename T>
inline void inc(T &x, T y) {
    x += y;
    if(x >= P) x -= P;
}

struct Matrix {
    int len, wid, s[M][M];

    inline void init() {
        len = wid = 0;
        memset(s, 0LL, sizeof(s));
    } 

    friend Matrix operator * (const Matrix &x, const Matrix &y) {
        Matrix res; res.init();
        res.len = x.len, res.wid = y.wid;
        for(int k = 0; k < x.wid; k++)
            for(int i = 0; i < x.len; i++)
                for(int j = 0; j < y.wid; j++)
                    inc(res.s[i][j], (int) (1LL * x.s[i][k] * y.s[k][j] % P));
        return res;
    }

} f, tra;

inline Matrix fpow(Matrix x, int y) {
    Matrix res; res.init();
    res.len = x.len, res.wid = x.wid;
    for(int i = 0; i < res.len; i++) res.s[i][i] = 1;

    for(; y > 0; y >>= 1) {
        if(y & 1) res = res * x;
        x = x * x;
    }

    return res;
}

inline int opp(int now) {
    return (now & 1) ? now + 1 : now - 1;
}

int main() {
    int tim, st, ed;
    read(n), read(m), read(tim), read(st), read(ed);
    for(int x, y, i = 1; i <= m; i++) {
        read(x), read(y);
        ++tot, from[tot] = x, to[tot] = y;
        ++tot, from[tot] = y, to[tot] = x;
    }

    f.init();f.len = 1, f.wid = 2 * m;
    for(int i = 1; i <= tot; i++)
        if(from[i] == st) ++f.s[0][i - 1];
    tra.init(); tra.len = tra.wid = 2 * m;
    for(int i = 1; i <= tot; i++)
        for(int j = 1; j <= tot; j++) {
            if(j == i || j == opp(i)) continue;
            if(to[i] == from[j]) 
                ++tra.s[i - 1][j - 1];
        }
    
    tra = fpow(tra, tim - 1);
    f = f * tra;

    int ans = 0;
    for(int i = 1; i <= tot; i++)
        if(to[i] == ed) inc(ans, f.s[0][i - 1]);
    
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2018-10-19 13:15  CzxingcHen  阅读(153)  评论(0编辑  收藏  举报