1. 程式人生 > >【BZOJ1706】relays 奶牛接力跑

【BZOJ1706】relays 奶牛接力跑

解析:

  矩陣快速冪。
  首先將起點終點離散化降至100100以內。
  考慮最裸的狀態轉移,令f[k][i][j]f[k][i][j]表示經過kk條邊從iijj的最短路長度,就有:f[k][i][j]=min{f[k1][i][mid]+a[mid][j]}f[k][i][j]=min\{f[k-1][i][mid]+a[mid][j ]\}
  這樣直接轉移複雜度是O(N1003)O(N*100^3)的考慮去掉kk部分後實際上就是一個矩陣乘法,那麼相當於是求初始矩陣的kk

次方,矩陣快速冪即可。
  總結:若發現遞推式很像矩陣乘法,並且滿足結合律,那麼就可以大膽地用矩陣乘法/矩陣快速冪求解。

程式碼:

#include <bits/stdc++.h>
using namespace std;

const int Max=105;
int n,m,s,t,k,rev[Max*10];
struct matrix{int a[Max][Max];}ans,v;

inline matrix mul(const matrix&a,const matrix&b)
{
	matrix c;
	memset(c.a,0x3f,sizeof(c.a));
	for
(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]); return c; } inline void solve() { while(k) { if(k&1) ans=mul(ans,v); k>>=1,v=mul(v,v); } } int main() { memset(v.a,0x3f,sizeof(v.a)); memset(ans.
a,0x3f,sizeof(ans.a)); scanf("%d%d%d%d",&k,&m,&s,&t); for(int i=1,a,b,c;i<=m;i++) { scanf("%d%d%d",&c,&a,&b); if(!rev[a]) rev[a]=++n; if(!rev[b]) rev[b]=++n; v.a[rev[a]][rev[b]]=v.a[rev[b]][rev[a]]=min(v.a[rev[b]][rev[a]],c); } for(int i=1;i<=n;i++) ans.a[i][i]=0; solve(); printf("%d",ans.a[rev[s]][rev[t]]); return 0; }