• Bzoj1875 [SDOI2009]HH去散步


    Time Limit: 20 Sec  Memory Limit: 64 MB
    Submit: 1518  Solved: 727

    Description

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

    Input

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

    Output

    一行,表示答案。

    Sample Input

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

    Sample Output

    4

    HINT

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

    Source

    数学问题 矩阵乘法

    矩阵乘法可以用来求图上两点之间的路径数。

    因为题目限制了不能走刚走过的路,所以用点到点的度数矩阵无法统计答案。

      ↑那么就改成统计边到边的转移方式。即从一条边走到另一条边的方案数。虚拟一个结点表示“起点所连的边(的集合)”,从该点只能出不能回。

    处理出度数矩阵以后,自乘t-1次(最后一步要走到B)。

    然后扫描从哪些边可以一步走到B,累加答案即可

     1 /*by SilverN*/
     2 #include<algorithm>
     3 #include<iostream>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 #include<vector>
     8 using namespace std;
     9 const int mod=45989;
    10 const int mxn=210;
    11 int read(){
    12     int x=0,f=1;char ch=getchar();
    13     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    14     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    15     return x*f;
    16 }
    17 struct edge{
    18     int v,nxt;
    19 }e[mxn<<2];
    20 int hd[mxn],mct=0;
    21 void add_edge(int u,int v){
    22     e[mct].nxt=hd[u];e[mct].v=v;hd[u]=mct;mct++;return;
    23 }
    24 int n,m,t,A,B;
    25 struct Mat{
    26     int a[mxn][mxn];
    27     void clear(){memset(a,0,sizeof a);return;}
    28     Mat operator * (Mat c){
    29         Mat res;memset(res.a,0,sizeof res.a);
    30         for(int i=0;i<=mct;i++)
    31             for(int j=0;j<=mct;j++)
    32                 for(int k=0;k<=mct;k++){
    33                     (res.a[i][j]+=a[i][k]*c.a[k][j])%=mod;
    34                 }
    35         return res;
    36     }
    37 }mp,s;
    38 Mat MatMul(Mat M,int k){
    39     Mat tmp;tmp.clear();
    40     for(int i=0;i<=mct;i++)tmp.a[i][i]=1;
    41     while(k){
    42         if(k&1)tmp=tmp*M;
    43         M=M*M;
    44         k>>=1;
    45     }
    46     return tmp;
    47 }
    48 int main(){
    49     int i,j,u,v;
    50     n=read();m=read();t=read();A=read();B=read();
    51     memset(hd,-1,sizeof hd);
    52     for(i=1;i<=m;i++){
    53         u=read();v=read();
    54         add_edge(u,v);
    55         add_edge(v,u);
    56     }
    57     for(i=hd[A];i>=0;i=e[i].nxt) s.a[mct][i]++;
    58     for(i=0;i<mct;i++){
    59         for(j=hd[e[i].v];j>=0;j=e[j].nxt){
    60             if(j==(i^1))continue;
    61             mp.a[i][j]++;
    62         }
    63     }
    64 /*    
    65     for(i=0;i<=mct;i++){
    66         for(j=0;j<=mct;j++){
    67             printf("%d ",mp.a[i][j]);
    68         }
    69         printf("
    ");
    70     }
    71 */    
    72     mp=MatMul(mp,t-1);
    73     s=s*mp;
    74     int ans=0;
    75     for(i=hd[B];i>=0;i=e[i].nxt){
    76         ans=(ans+s.a[mct][i^1])%mod;
    77     }
    78     printf("%d
    ",ans);
    79     return 0;
    80 }
    本文为博主原创文章,转载请注明出处。
  • 相关阅读:
    浏览器阻止window.open的解决方案
    汇总前端最最常用的JS代码片段
    检测是否是微信浏览器
    添加标签
    如何去掉textarea右下角的灰色角标?
    移动WEB前端开发资源整合
    图片文字水平居中(移动端)
    文本形式访客计数器
    如何处理加括号的四则混合运算表达式——基于二叉树的实现(Eclipse平台 Java版)
    深拷贝,浅拷贝
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/6506652.html
Copyright © 2020-2023  润新知