【BZOJ-4386】Wycieczki DP + 矩阵乘法
4386: [POI2015]Wycieczki
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 197 Solved: 49
[Submit][Status][Discuss]
Description
给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种。
将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。
Input
第一行包含三个整数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。
可能有重边。
Output
包含一行一个正整数,即第k短的路径的长度,如果不存在,输出-1。
Sample Input
6 6 11
1 2 1
2 3 2
3 4 2
4 5 1
5 3 1
4 6 3
1 2 1
2 3 2
3 4 2
4 5 1
5 3 1
4 6 3
Sample Output
4
HINT
长度为1的路径有1->2,5->3,4->5。
长度为2的路径有2->3,3->4,4->5->3。
长度为3的路径有4->6,1->2->3,3->4->5,5->3->4。
长度为4的路径有5->3->4->5。
Source
Solution
边权只有1,2,3三种,可以考虑拆点,那么只有边权为1的边了
那么显然可以DP,但是时间复杂度不允许
考虑用矩阵乘法去转移
这里比较优秀的方法是基于倍增的矩阵乘法
总复杂度是$O(n^{3}logK)$
答案会很大,乘爆longlong需要特判
这题细节非常多!
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; inline long long read() { long long x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 130 int id[MAXN][4],ID,N,M,B; long long K,V[MAXN*3]; struct MatrixNode{long long a[MAXN][MAXN];}a[100],b,c,tmp; MatrixNode operator * (const MatrixNode &A,const MatrixNode &B) { MatrixNode C; memset(C.a,0,sizeof(C.a)); for (int i=0; i<=ID; i++) for (int j=0; j<=ID; j++) for (int k=0; k<=ID; k++) if (A.a[i][k]&&B.a[k][j]) { if (A.a[i][k]<0 || B.a[k][j]<0) {C.a[i][j]=-1; break;} if (A.a[i][k]>K/B.a[k][j]) {C.a[i][j]=-1; break;} C.a[i][j]+=A.a[i][k]*B.a[k][j]; if (C.a[i][j]>K) {C.a[i][j]=-1; break;} } return C; } bool Check() { long long re=0; for (int i=0; i<=ID; i++) if (tmp.a[0][i] && V[i]) { if (tmp.a[0][i]<0) return 0; if (tmp.a[0][i]>K/V[i]) return 0; re+=tmp.a[0][i]*V[i]; if (re>K) return 0; } return re<K; } int main() { N=read(),M=read(),K=read(); for (int i=1; i<=N; i++) for (int j=0; j<=2; j++) id[i][j]=++ID; for (int i=1; i<=N; i++) { for (int j=0; j<=1; j++) a[0].a[id[i][j]][id[i][j+1]]++; a[0].a[0][id[i][0]]++; } a[0].a[0][0]++; for (int x,y,z,i=1; i<=M; i++) x=read(),y=read(),z=read(),a[0].a[id[y][z-1]][id[x][0]]++,V[id[y][z-1]]++; for (int i=0; (1LL<<i)<=K*3; B=++i); B--; for (int i=1; i<=B; i++) a[i]=a[i-1]*a[i-1]; long long ans=0; for (int i=0; i<=ID; i++) c.a[i][i]=1; for (int i=B; ~i; i--) { tmp=c*a[i]; if (Check()) ans|=(1LL<<i),memcpy(c.a,tmp.a,sizeof(tmp.a)); } ans++; if (ans>K*3) {puts("-1"); return 0;} else printf("%lld\n",ans); return 0; }
模拟赛的时候,没认真读题,以为是“魔法猪学院”类似的...等到半场才发现.....然后GG
——It's a lonely path. Don't make it any lonelier than it has to be.