zoukankan      html  css  js  c++  java
  • 【BZOJ】2165: 大楼

    【题意】从第0层开始有无穷层,每层有n个房间,给定矩阵A,A[i][j]表示从第x层的房间 i 可以跳到第x+A[i][j]层的房间 j (x任意),A[i][j]=0表示不能跳。初始在第0层第1个房间,求最少跳几次可以到达>=m层。n<=100,m<=10^18。

    【算法】矩阵快速幂

    【题解】我的写法好像和网上的不太一样……

    设$f_n[i]$表示跳n步在房间 i 的最高层数(这里全部的n和题目的n无关),考虑递推列向量$f_n$,设转移矩阵T,满足$T_{i,j}=A_{j,i}$,那么有:

    $$T imes f_n=f_{n+1}$$

    初始状态f0={1,0,0...0},那么写成幂形式:

    $$T^n imes f_0=f_n$$

    为了方便,容易发现$T^n$的最左一列就是$f_n$。

    我们要跳到$f_n$中包含>=m的数字为止,所以预处理所有$T^{2^i}$,倍增即可。

    复杂度O(n^3*log m+n log m)。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=101;
    const ll inf=1000000000000000000;
    ll m,c2[N],c[N][N],A[70][N][N],ans[N][N],ans2[N][N];
    int n;
    void multply(ll a[N][N],ll b[N][N],ll d[N][N]){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                c[i][j]=-inf;//
                for(int k=1;k<=n;k++){
                    c[i][j]=max(c[i][j],a[i][k]+b[k][j]);
                }
            }
        }
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)d[i][j]=c[i][j];
    }
    int main(){
        int T;scanf("%d",&T);
        while(T--){
            scanf("%d%lld",&n,&m);
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    scanf("%lld",&A[0][j][i]);
                    if(A[0][j][i]==0)A[0][j][i]=-inf;
                }
            }
            int tot=0;
            bool ok=0;
            for(int i=1;i<=n;i++)if(A[tot][i][1]>=m){ok=1;break;}
            if(!ok){
                while(1){
                    tot++;
                    multply(A[tot-1],A[tot-1],A[tot]);
                    bool ok=0;
                    for(int i=1;i<=n;i++)if(A[tot][i][1]>=m){ok=1;break;}
                    if(ok)break;
                }
            }
            c2[0]=1;
            for(int i=1;i<=tot-1;i++)c2[i]=c2[i-1]*2;
            ll ANS=c2[tot-1];
            for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)ans[i][j]=A[tot-1][i][j];
            for(int i=tot-2;i>=0;i--){
                multply(ans,A[i],ans2);
                bool ok=1;
                for(int j=1;j<=n;j++)if(ans2[j][1]>=m)ok=0;
                if(ok){
                    ANS+=c2[i];
                    for(int k=1;k<=n;k++)for(int l=1;l<=n;l++)ans[k][l]=ans2[k][l];//
                }
            }
            printf("%lld
    ",ANS+1);
        }
        return 0;
    }
    View Code

    注意T[i][j]=0时设为-inf,即不可达。

    网上的角度:关键在于题意的理解……给定n个点的有向图边权矩阵,0表示无边,求最少经过几条边使得路径长度>=m。

    经过指定条边后的最长路矩阵是很容易知道的,设$C^x$表示经过x条边后的最长路矩阵,$A$表示有向边权矩阵(0要设为-inf),那么:

    $$C^x(i,j)=max_k{C^{x-1}(i,k)+A(k,j)}$$

    所以C^x=A^x。

    预处理$C^{2^i}$,然后倍增到第一行出现>=m的数字为止。

    复杂度O(n^3 log m+n log m)。

  • 相关阅读:
    JavaScript类属性
    JavaScript实现通过的集合类
    Asp.net mvc自定义Filter简单使用
    [转] js实现html table 行,列锁定
    学习笔记:HTML5 Canvas绘制简单图形
    学习笔记:Asp.Net MVC更新部分页面
    Dom随手记
    页面刷新或关闭前警告
    C# 创建WebServices及调用方法
    常用Web Service汇总(天气预报、时刻表等)
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8780568.html
Copyright © 2011-2022 走看看