当前位置 博文首页 > Rabbit_Mua:「 洛谷 」P2151 [SDOI2009]HH去散步

    Rabbit_Mua:「 洛谷 」P2151 [SDOI2009]HH去散步

    作者:Rabbit_Mua 时间:2021-06-05 18:19

    「 洛谷 」P2151 [SDOI2009]HH去散步

    小兔的话

    欢迎大家在评论区留言哦~


    HH去散步

    题目限制

    • 内存限制:125.00MB
    • 时间限制:1.00s
    • 标准输入
    • 标准输出

    题目知识点

    • 动态规划 \(dp\)
    • 矩阵
      • 矩阵乘法
      • 矩阵加速
      • 矩阵快速幂
    • 思维
      • 构造

    题目来源

    「 洛谷 」P2151 [SDOI2009]HH去散步


    为了方便大家阅读通畅,题目可能略有改动,保证不会造成影响

    题目

    题目背景

    HH 有个一成不变的习惯,喜欢在饭后散步,就是在一定的时间内,走一定的距离
    同时, HH 是一个喜欢变化的人,她不会立刻沿着刚刚走过来的路走回去,她也希望每天走过的路径都不完全一样,她想知道每一天他究竟有多少种散步的方法

    题目描述

    现在 HH 送给你一张学校的地图,请你帮助她求出从地点 \(A\) 走到地点 \(B\) 一共有多少条长度为 \(T\) 的散步路径(答案对 \(45989\) 取模)

    格式

    输入格式

    输入共 \(M + 1\) 行:
    \(1\) 行:输入 \(5\) 个整数 \(N, \ M, \ T, \ A, \ B\)\(N\) 表示 学校里的路口的个数(编号为 \(0 \sim N - 1\)\(M\) 表示 学校里的道路的条数\(T\) 表示 HH 想要散步的距离\(A\) 表示 散步的出发点\(B\) 表示 散步的终点
    接下来 \(M\) 行:每行 \(2\) 个用空格隔开的整数 \(u_i, \ v_i\);表示 长度为 \(1\) 的第 \(i\) 条路 连接 路口 \(u_i\)路口 \(v_i\)

    输出格式

    输出共一行:表示你所求出的答案(对 \(45989\) 取模)

    样例

    样例输入

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

    样例输出

    4
    

    提示

    数据范围

    对于 \(30 \%\) 的数据:满足 \(N \leq 4, \ M \leq 10, \ T \leq 10\)
    对于 \(100 \%\) 的数据:满足 \(N \leq 50, \ M \leq 60, \ T \leq 2 ^ {30}, \ u_i \neq v_i\)


    思路

    这道题如果没有 她不会立刻沿着刚刚走过来的路走回去 的限制,就可以根据点与点的关系先构造出一个 \(n * n\) 的矩阵 \(\mathrm{x}\)\(\mathrm{x}[i][j]\) 表示从 \(i\)\(1\) 步到 \(j\) 的方案数),累乘 \(T\) 次(就是走了 \(T\) 步),就用矩阵快速幂优化既可以通过了
    现在就考虑加上这句话的限制后如何构造矩阵了


    分析

    考虑矩阵定义大致不变,即 \(\mathrm{x}[i][j]\) 表示从 \(i\)\(1\) 步到 \(j\) 的方案数
    由于有限制,就要记录刚刚走过来的路是哪一条
    不妨把每条边对应的 \(u_i\)\(v_i\) 拆成两个二元组 \(\mathrm{(node, id)}\),表示刚刚从第 \(\mathrm{id}\) 条路走到 \(\mathrm{node}\),也就是每条无向边 \((u_i \leftrightarrow, v_i)\) 分成两条有向边 \((u_i \to v_i)\)\((v_i \to u_i)\),其中 \(\mathrm{node}\) 表示当前这条有向边的终点,\(\mathrm{id}\) 表示与之对应的无向边的编号
    那么 \(\mathrm{x}[i][j] = 1\) 定义就是 \(i\) 个二元组\(1\) 步到 \(j\) 个二元组 的方案数
    其值只可能为 \(0\)\(1\)(因为只走了 \(1\) 步),其中值为 \(1\) 的条件就是 \(\mathrm{id}_i \neq \mathrm{id}_j\)\(\mathrm{node}_i\)\(\mathrm{node}_j\) 有一条边
    推出了矩阵,但是还有一个细节,就是第一步的方案数
    起始点是没有上一条边的,所以需要预处理一下(这里相当于先走了一次)
    预处理矩阵 \(\times\) 矩阵快速幂(\(T - 1\) 次,预处理走了一次)就可以得到最终的矩阵了
    最后把 起始点(超级源点)终点(可能有多个,因为分了边) 的路径加起来取模就可以了


    代码

    #include <cstdio>
    #include <cstring>
    
    int rint()
    {
    	int x = 0, fx = 1; char c = getchar();
    	while (c < '0' || c > '9') { fx ^= ((c == '-') ? 1 : 0); c = getchar(); }
    	while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
    	if (!fx) return -x;
    	return x;
    }
    
    const int MOD = 45989;
    
    const int MAX_N = 20;
    const int MAX_M = 60;
    
    int N, M, T, A, B, node;
    int e[MAX_M * 2 + 5][3];
    
    struct Matrix
    {
    	int mx[MAX_M * 2 + 5][MAX_M * 2 + 5];
    	
    	Matrix () { memset(mx, 0, sizeof(mx)); }
    	
    	void init() { for (int i = 0; i <= node; i++) mx[i][i] = 1; }
    	
    	Matrix operator * (const Matrix &rhs) const
    	{
    		Matrix res;
    		for (int i = 0; i <= node; i++)
    			for (int j = 0; j <= node; j++)
    				for (int k = 0; k <= node; k++)
    					res.mx[i][j] = (res.mx[i][j] + mx[i][k] * rhs.mx[k][j]) % MOD;
    		return res;
    	}
    } dp, quick;
    
    Matrix qpow(Matrix mx, int k)
    {
    	Matrix res; res.init();
    	while (k > 0)
    	{
    		if (k & 1) res = res * mx;
    		mx = mx * mx; k >>= 1;
    	}
    	return res;
    }
    
    int main()
    {
    	N = rint(), M = rint(), T = rint();
    	A = rint() + 1, B = rint() + 1;
    	for (int i = 1; i <= M; i++)
    	{
    		e[i][0] = rint() + 1, e[i][1] = rint() + 1;
    		e[i + M][0] = e[i][1], e[i + M][1] = e[i][0];
    		if (e[i][0] == A) ++dp.mx[0][i];
    		if (e[i + M][0] == A) ++dp.mx[0][i + M];
    	}
    	node = M << 1;
    	for (int i = 1; i <= node; i++)
    		for (int j = 1; j <= node; j++)
    			if (i + M != j && i - M != j && e[i][1] == e[j][0]) ++quick.mx[i][j];
    	int ans = 0;
    	Matrix res = dp * qpow(quick, T - 1);
    	for (int i = 1; i <= node; i++)
    		if (e[i][1] == B) ans = (ans + res.mx[0][i]) % MOD;
    	printf("%d\n", ans);
    	return 0;
    }
    
    

    bk
    下一篇:没有了