1. 程式人生 > >洛谷2886 [USACO07NOV]牛繼電器Cow Relays (矩陣乘法+Floyd)

洛谷2886 [USACO07NOV]牛繼電器Cow Relays (矩陣乘法+Floyd)

題目連結

一道很有紀念意義的題目啊qwq
感覺其實還不是很理解。

首先,根據題目的資料範圍,我們可以想到用 f l o y d floyd 去解決這個問題。

我們會發現,假設我們已經知道了一個經過 x

x 條路,到某個點的最短路矩陣 a a ,同時我們知道了每個點出發經過一條路到各個點的最短路矩陣 b b ,我們令 c
[ i ] [ j ] = m i n (
c [ i ] [ j ] , a [ i ] [ k ] + b [ k ] [ j ] ) c[i][j]=min(c[i][j],a[i][k]+b[k][j])
,新得到的矩陣,就應該對應的是經過 x + 1 x+1 條路徑到每個點的最短路。

那我們貌似可以通過 k k 次暴力合併,來得到答案。但顯然這樣的複雜度是不夠優秀的。

經過觀察,我們發現這個過程很像是兩個矩陣在進行矩陣乘法(一種變形)。

那麼我們可以直接選擇矩陣快速冪來優化這個過程。然後就可以把複雜度變到比較優秀的複雜度辣!

但是需要注意的一個小 t i p s tips 是,我們可以令初始的 a n s ans 等於一開始的 d i s dis 矩陣,然後只需要和 k 1 k-1 次方的矩陣合併即可。

QWQ
感覺細節還是很多啊

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
#define pb push_back
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 210;
struct Ju{
	int x,y;
	int a[maxn][maxn];
	Ju operator* (Ju b)
	{
		Ju ans;
		ans.x=x;
		ans.y=b.y;
		memset(ans.a,127/3,sizeof(ans.a));
		for (int k=1;k<=y;k++)
	  	  for (int i=1;i<=ans.x;i++)
		    for (int j=1;j<=ans.y;j++)
		      ans.a[i][j]=min(ans.a[i][j],a[i][k]+b.a[k][j]);
		return ans;
	}
};
Ju dis;
Ju qsm(Ju i,int j)
{
    Ju ans = i;
    while (j)
    {
  	  if (j&1) ans=ans*i;
      i=i*i;	
	  j>>=1;
    }
    return ans;
}
int tot;
int x[maxn],y[maxn],w[maxn];
int n,m;
vector<int> v;
int k;
int s,t;
int getnum(int val)
{
	return lower_bound(v.begin(),v.end(),val)-v.begin()+1;
}
signed main()
{
  k=read();
  m=read();
  s=read(),t=read();
  v.pb(s);
  v.pb(t);
  for (int i=1;i<=m;i++)
  {
  	w[i]=read(),x[i]=read(),y[i]=read();
  	v.pb(x[i]);
  	v.pb(y[i]);
  } 
  sort(v.begin(),v.end());
  n=unique(v.begin(),v.end())-v.begin();
  v.resize(n);
  n++;
  s=getnum(s);
  t=getnum(t);
  memset(dis.a,0x3f,sizeof(dis.a));
  //for(int i=1;i<=n;i++) dis.a[i][i]=0;
  dis.x=n;
  dis.y=n;
  for (int i=1;i<=m;i++)
  {
  	x[i]=getnum(x[i]);
  	y[i]=getnum(y[i]);
  	dis.a[x[i]][y[i]]=min(dis.a[x[i]][y[i]],w[i]);
  	dis.a[y[i]][x[i]]=dis.a[x[i]][y[i]];
  }
  dis=qsm(dis,k-1);
  cout<<dis.a[s][t]<<endl;
  return 0;
}