• 【bzoj4386】[POI2015]Wycieczki【矩阵快速幂】【倍增】


    vjudge题目传送门
    luogu题目传送门
    题解
    首先,我们考虑如何统计所有边权都是1的经过x条边的路径总数。很简单,构造转移矩阵我们只需要相邻的两个点u->v,(u,v)++,再设一个计数器代表路径总数,(u,计数器)++,最后再 (计数器,计数器)=1。初始矩阵就是(1,1)=(1,2)…=(1,n)=1。然后快速幂。
    但是如果权值有2,3呢?蒟蒻从题解上get到一个很妙的想法:把每个点搞成3个,u1,u2,u3。
    对于边(u,v)边权为1:
    u1->v1
    边权为2:
    u1->u2->v1
    边权为3:
    u1->u2->u3->v1
    现在明白是什么意思了吧!
    这样每条边的权值都是1了。
    现在我们可以很方便地求出经过x条边的路径总数。
    接下来,有一个想法是二分。但是这里有一个更妙的想法:倍增。我们二进制拆分,从高位到低位统计答案。细节自己想一想吧。
    生命中的第一个bzoj rank1,第二个luogu rank1!嗨森~
    倒是没怎么卡。就是矩阵乘法改了一下枚举的顺序,0就直接跳过。纪念一下。
    这里写图片描述
    这里写图片描述
    233 wyc大佬莫名乱入
    代码

    #include<cstdio>
    #include<cstring>
    typedef long long ll;
    const int N=125;
    int n,m,u,v,d,t;
    ll k,s,bg[N][N],base[N][N],tmp[N][N],now[N][N],mul[64][N][N];
    bool flag;
    void times(ll a[][N],ll b[][N],ll c[][N]){
        flag=true;
        memset(tmp,0,sizeof(tmp));
        for(int i=1;i<=3*n+1;i++){
            for(int l=1;l<=3*n+1;l++){
                if(!a[i][l]){
                    continue;
                }
                for(register int j=1;j<=3*n+1;j++){
                    tmp[i][j]+=a[i][l]*b[l][j];
                }
                if(i==1&&tmp[i][3*n+1]>=k){
                    flag=false;
                }
            }
        }
        for(int i=1;i<=3*n+1;i++){
            for(int j=1;j<=3*n+1;j++){
                c[i][j]=tmp[i][j];
            }
        }
    }
    int main(){
        scanf("%d%d%lld",&n,&m,&k);
        for(int i=1;i<=n;i++){
            bg[1][i]=1;
            base[i][i+n]=1;
            base[i+n][i+2*n]=1;
        }
        while(m--){
            scanf("%d%d%d",&u,&v,&d);
            base[u+(d-1)*n][v]++;
            base[u+(d-1)*n][3*n+1]++;
        }
        base[3*n+1][3*n+1]=1;
        memcpy(mul[0],base,sizeof(base));
        for(;t<=63;t++){
            if(t){
                times(mul[t-1],mul[t-1],mul[t]);
            }
            times(bg,mul[t],now);
            if(!flag||now[1][3*n+1]>=k){
                break;
            }
        }
        t--;
        if(t==63&&now[1][3*n+1]<k){
            puts("-1");
            return 0;
        }
        for(int i=t;i>=0;i--){
            times(bg,mul[i],now);
            if(flag&&now[1][3*n+1]<k){
                s+=(1LL<<i);
                memcpy(bg,now,sizeof(now));
            }
        }
        printf("%lld
    ",s+1);
        return 0;
    }
  • 相关阅读:
    mysql保存中文乱码的原因和解决办法
    NetSetMan IP地址切换工具
    使用批处理文件,自动设置计算机IP地址
    神逸之作:国产快速启动软件神品ALTRun
    Apache详细介绍
    利用sqoop对mysql执行DML操作
    Mysql定时清空表
    azkaban group分组,权限
    azkaban使用
    sqoop无法导出parquet文件到mysql
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476853.html
Copyright © 2020-2023  润新知