bzoj4386 Wycieczki

题目描述

给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种。
将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。

输入

第一行包含三个整数n,m,k(1<=n<=40,1<=m<=1000,1<=k<=10^18)。
接下来m行,每行三个整数u,v,c(1<=u,v<=n,u不等于v,1<=c<=3),表示从u出发有一条到v的单向边,边长为c。
可能有重边。

输出

包含一行一个正整数,即第k短的路径的长度,如果不存在,输出-1。

样例输入

6 6 11
1 2 1
2 3 2
3 4 2
4 5 1
5 3 1
4 6 3

样例输出

4


solution

考虑只有边权为1的图。

拿一个矩阵G[i][j]表示i到j有多少走法。他的x次幂就是i走恰好x步到j的情况。

那么小于等于x的怎么求呢。

可以加一个计数点。把所有点向计数点连边,再加一个自环,也就是i-1步的方案也算进i步的方案。

现在考虑边权123

把每个点新建2个虚点,分别为 i+n i+n+n

如果i~j有x的边

连i+(x-1)*n~j

注意爆ll

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll unsigned long long
using namespace std;
int N,n,m;
ll goal;
ll Ans;
struct node{
    ll v[125][125];
    void cle(){
        for(int i=1;i<=N;i++)
    for(int j=1;j<=N;j++)v[i][j]=0;
    }
}G,A[70],ans,tmp;
node operator *(node A,node B){
    node C;C.cle();
    for(int i=1;i<=N;i++)
    for(int j=1;j<=N;j++){
        for(int k=1;k<=N;k++){
            C.v[i][j]+=A.v[i][k]*B.v[k][j];
            if(C.v[i][j]>1e19){C.v[0][0]=-1;return C;}
        }
    }
    return C;
}
bool pd(){
    if(tmp.v[0][0]<0){return 0;}
    ll sum=0;
    for(int i=1;i<=n;i++){
        sum+=(tmp.v[i][N]-1);
        if(sum>1e19)return 0;
        //cout<<tmp.v[i][N]<<' ';
    }
    //cout<<"sum "<<sum<<endl;
    return sum<goal;
}
int main(){
    cin>>n>>m>>goal;N=n+n+n+1;//goal+=n;
    for(int i=1;i<=n;i++){
        G.v[i][i+n]=G.v[i+n][i+n+n]=1;
        G.v[i][N]=1;
    }
    G.v[N][N]=1;
    for(int i=1,t1,t2,t3;i<=m;i++){
        scanf("%d%d%d",&t1,&t2,&t3);
        G.v[t1+(t3-1)*n][t2]++;
    }
    A[0]=G;

    for(int i=1;i<=63;i++)A[i]=A[i-1]*A[i-1];
    for(int i=1;i<=n;i++)ans.v[i][i]=1;
    for(int i=63;i>=0;i--){
        tmp=ans*A[i];
        if(pd())ans=tmp,Ans += (1ll << i);
        if(i==63&&pd()){puts("-1");return 0;}
    }
    cout<<Ans<<endl;
    return 0;
}
/*
2 1 1
1 2 1

*/
View Code

 

 

posted @ 2019-02-19 21:17  liankewei123456  阅读(259)  评论(0编辑  收藏  举报