洛谷2886 [USACO07NOV]牛繼電器Cow Relays (矩陣乘法+Floyd)
阿新 • • 發佈:2018-12-23
一道很有紀念意義的題目啊qwq
感覺其實還不是很理解。
首先,根據題目的資料範圍,我們可以想到用 去解決這個問題。
我們會發現,假設我們已經知道了一個經過 條路,到某個點的最短路矩陣 ,同時我們知道了每個點出發經過一條路到各個點的最短路矩陣 ,我們令 ,新得到的矩陣,就應該對應的是經過 條路徑到每個點的最短路。
那我們貌似可以通過 次暴力合併,來得到答案。但顯然這樣的複雜度是不夠優秀的。
經過觀察,我們發現這個過程很像是兩個矩陣在進行矩陣乘法(一種變形)。
那麼我們可以直接選擇矩陣快速冪來優化這個過程。然後就可以把複雜度變到比較優秀的複雜度辣!
但是需要注意的一個小 是,我們可以令初始的 等於一開始的 矩陣,然後只需要和 次方的矩陣合併即可。
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;
}