题解
先用Floyd预处理各个点小道的距离,每个点有两个状态:从小道到达d1[u]
和从大道到达d2[u]
。转移方式为:
d1[u]:d2[v]+w; 大道到达的点+小道
d2[u]: d2[u]+w; 大道到达的点+大道
d2[u]: d1[v]+w; 小道到达的点+大道
为什么小道的点不能从小道到达的点+小道呢?
因为我们已经预处理过了,比如u->v->w,其中(u,v),(v,w)都是小道,u是大道到达的点,d[w] = d[v]+w;的话是不正确的,因为连续走了两条小道。所以,小道到达的点一定是从一个大道到达的点走一连串小道到达的。
#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 500+5;
const ll INF = 1e18;
int n,m;
ll g1[maxn][maxn],g2[maxn][maxn];
ll d1[maxn],d2[maxn];
int cnt[maxn], inq[maxn];
void spfa() {
// memset(d1,INF,sizeof d1);
// memset(d2,INF,sizeof d2);
d1[1] = 0;
d2[1] = 0;
queue<int>que;
que.push(1);
inq[1] = true;
// cnt[1] = 1;
while(!que.empty()) {
int u = que.front();
que.pop();
// cout << u << " " << d2[u]<<" "<<d1[u] << endl;
inq[u] = false;
for(int i = 1; i <= n; ++i) {
if(g2[u][i] != INF) {
if(d2[i] > d2[u]+g2[u][i]) {
d2[i] = d2[u]+g2[u][i];
if(!inq[i]) {
inq[i] = true;
que.push(i);
}
}
if(d2[i] > d1[u]+g2[u][i]) {
d2[i] = d1[u]+g2[u][i];
if(!inq[i]) {
inq[i] = true;
que.push(i);
}
}
}
if(g1[u][i] != INF) {
ll v = g1[u][i]*g1[u][i];
// cout << v << endl;
if(d1[i] > d2[u]+v) {
d1[i] = d2[u]+v;
if(!inq[i]) {
inq[i] = true;
que.push(i);
}
}
}
}
}
cout << min(d1[n],d2[n]) << endl;
}
int main() {
// memset(g1,INF,sizeof g1);
// memset(g2,INF,sizeof g2);
scanf("%d%d", &n, &m);
for(int i = 0; i <= n; ++i) {
d1[i] = d2[i] = INF;
for(int j = 0; j <= n; ++j) {
g1[i][j] = g2[i][j] = INF;
}
}
int t,a,b,c;
for(int i = 0; i < m; ++i) {
scanf("%d%d%d%d", &t, &a, &b, &c);
if(t == 1 && g1[a][b] > c)
g1[a][b] = g1[b][a] = c;
else if(t == 0 && g2[a][b] > c)
g2[a][b] = g2[b][a] = c;
}
for(int i = 1; i <= n; ++i)
for(int j = i+1; j <= n; ++j)
for(int k = 1; k <= n; ++k){
if(k == i || j == k)
continue;
if(g1[i][j] > g1[i][k]+g1[k][j])
g1[j][i] = g1[i][j] = g1[i][k]+g1[k][j];
// cout << g1[i][j] << endl;
}
spfa();
return 0;
}