[SDOI2009]HH去散步 题解

[SDOI2009]HH去散步 题解

题目地址:洛谷:【P2151】[SDOI2009]HH去散步 – 洛谷、BZOJ:Problem 1875. — [SDOI2009]HH去散步

题目描述

HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。
现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径

题意简述

给你一个无向图,求从a走到b且长度为t的路径总数,每条边经过次数不限,但是不能连续双向经过同一条边。

输入输出格式

输入格式:
第一行:五个整数N,M,t,A,B。其中N表示学校里的路口的个数,M表示学校里的 路的条数,t表示HH想要散步的距离,A表示散步的出发点,而B则表示散步的终点。
接下来M行,每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。数据保证Ai != Bi,但 不保证任意两个路口之间至多只有一条路相连接。 路口编号从0到N − 1。 同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。 答案模45989。

输出格式:
一行,表示答案。

输入输出样例

输入样例#1:

4 5 3 0 0
0 1
0 2
0 3
2 1
3 2

输出样例#1:

4

说明

对于30%的数据,N ≤ 4,M ≤ 10,t ≤ 10。
对于100%的数据,N ≤ 50,M ≤ 60,t ≤ 2^30,0 ≤ A,B

题解

参考资料:题解 P2151 【[SDOI2009]HH去散步】 – roufaen 的博客 – 洛谷博客
可以考虑用边DP,dp[i][j]表示已经走出距离i,且最后走过的一条边是j的方案数。显然这里需要把无向边拆成两条有向边考虑。转移也很显然,对于一条边i(u, v)及对应的状态dp[t][i],可以从j(w, u)对应的dp[t-1][j]转移过来,不过要判断i和j在原来的无向图中是否为同一条边。
我们观察到t的范围很大,而上述转移在两个距离之间显然是固定的线性变换,可以考虑使用矩阵快速幂加速转移。优化后的复杂度为O(4m^2 \log t)

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) 
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 205, MO = 45989;

int n, m;

struct Matrix {
    int a[MAXN][MAXN];
    Matrix() {
        memset(a, 0, sizeof(a));
    }
    inline Matrix operator*(const Matrix &rhs) const {
        Matrix res;
        for(int i = 0; i < m << 1; i++) {
            for(int j = 0; j < m << 1; j++) {
                for(int k = 0; k < m << 1; k++) {
                    res.a[i][j] = (res.a[i][j] + a[i][k] * rhs.a[k][j]) % MO;
                }
            }
        }
        return res;
    }
    inline Matrix& operator*=(const Matrix &x) {
        return *this = *this * x;
    }
};

inline Matrix fpow(Matrix x, LL k) {
    Matrix res;
    for(int i = 0; i < m << 1; i++) {
        res.a[i][i] = 1;
    }
    while(k) {
        if(k & 1) res *= x;
        x *= x;
        k >>= 1;
    }
    return res;
}

struct Edge {
    int u, v;
} edges[MAXN];

LL t;
int a, b;
Matrix def, trans;

int main() {
    n = readint(); m = readint(); t = readint(); a = readint(); b = readint();
    for(int i = 0, u, v; i < m; i++) {
        u = readint(); v = readint();
        edges[i << 1] = Edge {u, v};
        edges[i << 1 | 1] = Edge {v, u};
    }
    for(int i = 0; i < m << 1; i++) {
        def.a[i][0] = (edges[i].u == a);
    }
    for(int i = 0; i < m << 1; i++) {
        for(int j = 0; j < m << 1; j++) {
            trans.a[j][i] = (edges[i].v == edges[j].u && i != (j ^ 1));
        }
    }
    trans = fpow(trans, t - 1);
    def = trans * def;
    int ans = 0;
    for(int i = 0; i < m << 1; i++) {
        if(edges[i].v == b) ans = (ans + def.a[i][0]) % MO;
    }
    printf("%d", ans);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据