baoj1875 HH去散步 【矩阵转移】

Brief description :

给定一个可能重边但没有自环的无向图,要求计算 A, B 两点之间步数为 t 的方案数。答案模 45989。

(可以经过某个点某条边多次,但是不可以立即沿着同一条边折返。)

(.. N <= 20, M <= 60, t <= 2^30 ..)

Analyse :

由于“不会沿着同一条边折返”,因此从 A 点经过 k 步後的状态仅与最后一步所走的边和它的方向有关。

如果将每条无向边拆成两条有向边,那么仅于边有关。

用 i==(j^1),排除立即走回边。

比如: 2  0010(u1->v1)                                                    4  0100(u2->v2)

3  0011(v1->u1)                                                    5  0101(v2->u2)

---->   (2&1)==3  (3&1)==2                                 ----> (4&1)==5  (5&1)==4

友链:http://www.shuizilong.com/house/archives/sdoi-2009-hh%E5%8E%BB%E6%95%A3%E6%AD%A5/

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define INF 0x7fffffff
#define SUP 0x80000000
#define _p 45989
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;

typedef long long LL;
const int N=100007;

struct Edge{                 //邻接表节点
    int to,next;
}E[133];
int head[55];
int tot=2;

void add_(int u,int v)
{
    E[tot].to=v;
    E[tot].next=head[u];
    head[u]=tot++;
}

struct Matrix{
    int mat[133][133];
    Matrix()
    {
        mem(mat,0);
    }
    friend Matrix operator *(Matrix a,Matrix b)   //非成员函数重载
    {
        Matrix ans;
        for(int i=1;i<=tot;i++)
        {
            for(int j=1;j<=tot;j++)
            {
                for(int k=1;k<=tot;k++)
                    ans.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%_p,ans.mat[i][j]%=_p;
            }

        }
        return ans;
    }

    friend Matrix operator^(Matrix a,int b)
    {
        Matrix ans;
        for(int i=1;i<=tot;i++) ans.mat[i][i]=1;
        while(b)
        {
            if(b&1) ans=ans*a;    //出去回边
            a=a*a;
            b>>=1;
        }
        return ans;
    }
};

int main()
{
    int n,m,t,a,b;
    while(scanf("%d%d%d%d%d",&n,&m,&t,&a,&b)==5)
    {
        mem(head,-1);
        int u,v;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            add_(u,v);
            add_(v,u);
        }

        Matrix st,tt;
        for(int i=head[a];i!=-1;i=E[i].next) st.mat[1][i]=1;   //初始的一步

        vector<int> en;
        for(int i=2;i<tot;i++)    //转移的矩阵
        {
            int to=E[i].to;
            if(to==b) en.push_back(i);
            for(int j=head[to];j!=-1;j=E[j].next)
            {
                if(i==(j^1)) continue;
                tt.mat[i][j]=1;
            }
        }

        int ans=0;
        st=st*(tt^(t-1));       // 1+(t-1)==t步  ,即初始一步+转移t-1次
        for(int i=0;i<en.size();i++)
        {
            ans=(ans+st.mat[1][en[i]])%_p;
        }
        printf("%d\n",ans);
    }

    return 0;
}
时间: 2024-10-06 00:53:47

baoj1875 HH去散步 【矩阵转移】的相关文章

bzoj 1875: [SDOI2009]HH去散步 -- 矩阵乘法

1875: [SDOI2009]HH去散步 Time Limit: 20 Sec  Memory Limit: 64 MB Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又因为HH是个喜欢变化的人,所以他每 天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法. 现在给你学校的地图(假设每条路的长度都 是一样的都是1),问长度为t,从给定

BZOj-1875: [SDOI2009]HH去散步 (矩阵快速幂)

1875: [SDOI2009]HH去散步 Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 1999  Solved: 980[Submit][Status][Discuss] Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又因为HH是个喜欢变化的人,所以他每 天走过的路径都不完全一样,他想知道他究竟

【bzoj1875】[SDOI2009]HH去散步 矩阵乘法

题目描述 一张N个点M条边的无向图,从A走到B,要求:每一次不能立刻沿着上一次的边的反方向返回.求方案数. 输入 第一行:五个整数N,M,t,A,B. N表示学校里的路口的个数 M表示学校里的路的条数 t表示HH想要散步的距离 A表示散步的出发点 B则表示散步的终点. 接下来M行 每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路. 数据保证Ai != Bi,但不保证任意两个路口之间至多只有一条路相连接. 路口编号从0到N -1. 同一行内所有数据均由一个空格隔开,行首行尾没有多余空格.没有多

BZOJ 1875 SDOI 2009 HH去散步 矩阵乘法优化DP

题目大意:给出一张无向图,求从A到B走k步(不能走回头路)的方案数.(k <= 2^30) 思路:看到k的范围就知道是矩阵乘法了.关键是不能走回头路怎么构造.正常的方法构造点的转移不能避免这个问题,就用边来构造.只要保证不经过自己^1的边就可以保证不走回头路了. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX

bzoj1875 [SDOI2009]HH去散步 矩阵快速幂

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=1875 题解 如果没有这个"不能立刻沿着刚刚走来的路走回",那么这个题就是一个常规的矩阵乘法. 考虑一下这个限制怎么解决.因为限制的是边,我们不妨考虑和边有关的矩阵. 首先把一条无向边拆成两个有向边,如果边 \(A\) 的终点和边 \(B\) 的起点相同,那么我们就说从边 \(A\) 通向边 \(B\).但是,同源的有向边(也就是从同一条无向边拆成的两条有向边)之间不能建边. 但是为

BZOJ 1875: [SDOI2009]HH去散步( dp + 矩阵快速幂 )

把双向边拆成2条单向边, 用边来转移...然后矩阵乘法+快速幂优化 --------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MOD = 45989; const int

【bzoj1875】【SDOI2009】【HH去散步】

1875: [SDOI2009]HH去散步 Time Limit: 20 Sec Memory Limit: 64 MB Submit: 932 Solved: 424 [Submit][Status][Discuss] Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多

BZOJ1875: [SDOI2009]HH去散步

1875: [SDOI2009]HH去散步 Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 620  Solved: 265[Submit][Status] Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法.

BZOJ 1875[SDOI2009]HH去散步

题面: 1875: [SDOI2009]HH去散步 Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 1750  Solved: 851[Submit][Status][Discuss] Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又因为HH是个喜欢变化的人,所以他每 天走过的路径都不完全一样,他想知