给出一个n个点m条边的无向图 (边带权) 和该图的一棵最短路树, 对于除源点1外的每个点i, 求1到i的最短路, 要求不经过i在最短路树上的父边. (n≤4000, m≤10^5, 1≤边权≤10^5) 假设点
量化一下. 设 multiset
就好, 合并可以启发式.
看了下题解......直接轻重链剖分+线段树就可以了:
轻重链剖分+线段树的时间复杂度是
据说本题数据是随机生成的, 树高是期望
typedef multiset<int> Set;
const int N = 4001;
struct Edge
{
int to, w;
};
vector<int> L[N];
vector<Edge> G[N], T[N];
int dfn, st[N], ed[N], fa[N], d[N], l[N], sz[N], mx[N], top[N], ans[N];
Set S[N];
void dfs_1(int u)
{
sz[u] = 1;
rep (i, 0, T[u].size())
{
int v = T[u][i].to;
if (v == fa[u]) continue;
d[v] = d[u] + 1;
l[v] = l[u] + T[u][i].w;
fa[v] = u;
dfs_1(v);
sz[u] += sz[v];
if (sz[v] > sz[mx[u]]) mx[u] = v;
}
}
void dfs_2(int u, int t)
{
st[u] = ++dfn;
top[u] = t;
if (mx[u])
{
dfs_2(mx[u], t);
rep (i, 0, T[u].size())
{
int v = T[u][i].to;
if (v != fa[u] && v != mx[u]) dfs_2(v, v);
}
}
ed[u] = ++dfn;
}
inline int lca(int x, int y)
{
while (top[x] != top[y])
{
if (d[top[x]] > d[top[y]]) swap(x, y);
y = fa[top[y]];
}
return d[x] > d[y] ? y : x;
}
void dfs(int u, Set& s)
{
rep (i, 0, T[u].size())
{
int v = T[u][i].to;
if (v != fa[u]) dfs(v, v == mx[u] ? s : S[v]);
}
rep (i, 0, T[u].size())
{
int v = T[u][i].to;
if (v != fa[u] && v != mx[u])
{
s.insert(S[v].begin(), S[v].end());
S[v].clear();
}
}
rep (i, 0, L[u].size())
{
Set::iterator it = s.find(L[u][i]);
s.erase(it);
}
rep (i, 0, G[u].size())
{
int v = G[u][i].to, t = l[v] + G[u][i].w + l[u];
if (st[v] >= st[u] && ed[v] <= ed[u]) continue;
s.insert(t);
L[lca(v, u)].push_back(t);
}
ans[u] = s.empty() ? -1 : *s.begin() - l[u];
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
rep (i, 0, m)
{
int a, b, l, t;
scanf("%d%d%d%d", &a, &b, &l, &t);
if (a == b) continue;
vector<Edge> *adj = t ? T : G;
adj[a].push_back((Edge){b, l});
adj[b].push_back((Edge){a, l});
}
dfs_1(1);
dfs_2(1, 1);
dfs(1, S[1]);
rep (i, 2, n+1)
printf("%d ", ans[i]);
puts("");
return 0;
}