• P2151 [SDOI2009]HH去散步


    题目描述

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

    现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径

    输入输出格式

    输入格式:

    第一行:五个整数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

    Solution:

      本题矩阵加速dp。

      一眼想到dp,因为刚走过的边不能立即返回,若按点去定义状态就会不好判断重边和刚走过的边的情况,解决办法是按边去定义状态,先把无向边拆成有向边,设$f[i][j]$表示到了第$i$条边走了$j$距离的方案数,于是$f[i][j]=sum f[k][j-1]$(其中第$k$条边能到第$i$条边,且$i,k$不是同属一条无向边)。

      于是就能矩阵优化dp了,初始矩阵就是个$1*2m$的矩阵,其中是$A$的出边都标记为1,然后转移矩阵是$2m*2m$的矩阵,由入边$i$向出边$j$转移,所以使得$matrix[i][j]++$即可。

      最后答案就统计到达$B$的入边的方案数之和就好了。

    代码:

    /*Code by 520 -- 9.11*/
    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define RE register
    #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
    #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
    #define Clr(p) memset(&p,0,sizeof(p))
    using namespace std;
    const int mod=45989;
    int n,m,t,A,B,to[130],net[130],h[130],cnt=1;
    struct matrix{int a[130][130],r,c;};
    
    il matrix Mul(matrix x,matrix y){
        matrix tp; Clr(tp);
        tp.r=x.r,tp.c=y.c;
        For(i,0,x.r) For(j,0,y.c) For(k,0,x.c)
            tp.a[i][j]=(tp.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod;
        return tp;
    }
    
    il void solve(int k){
        matrix tp,ans; Clr(tp),Clr(ans);
        ans.r=0,ans.c=tp.r=tp.c=cnt;
        for(RE int i=h[A];i;i=net[i]) ans.a[0][i]=1;
        For(u,0,n-1) for(RE int i=h[u];i;i=net[i]) {
            RE int v=to[i];
            for(RE int j=h[v];j;j=net[j])
                if((j^1)!=i) tp.a[i][j]++;
        }
        while(k){
            if(k&1) ans=Mul(ans,tp);
            k>>=1;
            tp=Mul(tp,tp);
        }
        int tot=0;
        for(RE int i=h[B];i;i=net[i]) tot+=ans.a[0][(i^1)];
        cout<<tot%mod;
    }
    
    il void add(int u,int v){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt;}
    
    int main(){
        ios::sync_with_stdio(0);
        cin>>n>>m>>t>>A>>B;
        int u,v;
        For(i,1,m) cin>>u>>v,add(u,v),add(v,u);
        solve(t-1);
        return 0;
    }
  • 相关阅读:
    [NOIP2008] 传纸条
    [NOIP2006] 能量项链
    [poj2393] Yogurt factory
    [poj3069] Saruman's Army
    [NOIP2011] 观光公交
    [NOIP2010] 关押罪犯
    [洛谷2744] 量取牛奶
    [poj3281] Dining
    关于几类STL容器的swap复杂度问题
    折半法
  • 原文地址:https://www.cnblogs.com/five20/p/9633562.html
Copyright © 2020-2023  润新知