做法很多,本人使用线段树。
原图可以看作分层 DAG,每层结点有 \(k\) 个,而 \(k\le 5\)。
假设每层的点编号 \(0\sim k-1\)。从 \(l\) 到 \(r\) 层的路径,在线段树上用区间 \([l,r-1]\) 表示。线段树上每个结点都存储表示最段路的矩阵,合并时使用 Floyd。
另外,需要特判询问中是否两个点在同一层里,这样一定不连通。
// Title: Toll
// Source: P6573
// Author: Jerrywang
#include <bits/stdc++.h>
#define F first
#define S second
#define pii pair<int, int>
#define ll long long
#define rep(i, s, t) for(int i=s; i<=t; ++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
const int N=50010, M=5;
using namespace std;
int k, n, m, T; vector<pii> g[N][M];
struct node
{
int l, r, d[M][M];
void clr() {memset(d, 0x3f, sizeof d);}
} t[N<<2];
#define lc p<<1
#define rc p<<1|1
void up(node &a, node l, node r)
{
a.clr();
rep(w, 0, k-1) rep(u, 0, k-1) rep(v, 0, k-1)
a.d[u][v]=min(a.d[u][v], l.d[u][w]+r.d[w][v]);
}
void build(int p, int l, int r)
{
t[p]={l, r};
if(l==r)
{
t[p].clr();
rep(u, 0, k-1) for(auto [v,w]:g[l][u])
t[p].d[u][v]=w;
return;
}
int m=l+r>>1;
build(lc, l, m), build(rc, m+1, r);
up(t[p], t[lc], t[rc]);
}
node query(int p, int l, int r)
{
if(l<=t[p].l && t[p].r<=r) return t[p];
int m=t[p].l+t[p].r>>1; node res;
if(r<=m) return query(lc, l, r);
if(l>m) return query(rc, l, r);
up(res, query(lc, l, r), query(rc, l, r));
return res;
}
int main()
{
#ifdef Jerrywang
freopen("E:/OI/in.txt", "r", stdin);
#endif
scanf("%d%d%d%d", &k, &n, &m, &T);
rep(i, 1, m)
{
int u, v, w; scanf("%d%d%d", &u, &v, &w);
g[u/k][u%k].push_back({v%k, w});
}
build(1, 0, n);
while(T--)
{
int u, v; scanf("%d%d", &u, &v);
int lu=u/k, lv=v/k;
if(lu==lv) {puts("-1"); continue;}
node t=query(1, lu, lv-1);
int res=t.d[u%k][v%k];
printf("%d\n", res==0x3f3f3f3f?-1:res);
}
return 0;
}
标签:int,res,d%,rc,BalticOI,query,2017,Toll,define
From: https://www.cnblogs.com/jerrywang2009/p/18064346