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 */