题目(洛谷上没有就懒得找了):给出一个 n 个点 m 条边的无向图, n 个点的编号从 1∼n , 定义源点为 1。定义最短路树如下:从源点 1 经过边集 T到任意一点 i 有且仅有一条路径, 且这条路径是整个图 1 到 i 的最短路径, 边集 T 构成最短路树。
给出最短路树, 求对于除了源点 1 外的每个点 i , 求最短路, 要求不经过给出的最短路树上的 1 到 i 的路径的最后一条边。
对于 100% 的数据, n≤4000,m≤100000,1≤li≤100000
emmm,n≤4000确定不考虑一发暴力?好吧,这道题只要你常数够好,卡过是完全没问题的(瞎说的,别去试,dijikstra堆优化是O((n + m)logn))。暴力很简单:枚举删边,每删一次,跑一边最短路,复杂度O(n * (n + m)logn)。我们顺着暴力的思路,考虑如何计算对于每一个点的最短路。首先我们可以发现,这里的最短路一定不能全由树边(树边:在最短路树中的边)组成,那么一定为根节点 -> 某一点(通过树边) -> 另一点(通过一条非树边,两条一定不是最优)->当前点(可以在上一步的时候就到,也可以由上一步通过一些反向树边到)。如图:(颜色较深的为树边)
如更新节点2:由1->5->2或由1->5->4->2。
现在,我们知道了最短路是如何更新的,但,我们发现一个问题,复杂度过不去,我们总不能直接对每一个点枚举所有非树边吧。(复杂度O(n*m)只比暴力略优)这是我们想要优化这个复杂度,显然m是没有办法改变的(1.需要每一条非树边保证答案是最优 2.对于非树边,不具有可以整体操作的方法),那么我们对于n又该怎么优化呢?这时,我们转换一下思路,对于每一条非树边,它其实并不能更新所有点的最短路,若该边为(u,v),那么他只能更新(u -> lca(u , v) -> v)上的节点(易证,自己画一下图就明白了)。(u -> lca(u , v) -> v)?这是个好东西,不就是u -> v的路径上的点吗,看到这,想必大家都明白了,没错,就是树链剖分。
(不会的同志可以去看看我的博客,写得很详细,传送门:https://blog.csdn.net/weixin_43790474/article/details/84556647 )
那么,想到了树链剖分,这道题就很简单了。就是枚举非树边,然后,对于每条非树边,用d[u] + d[v] - d[lca(u , v)] - d[x]来更新路径上的点,既每个点维护d[u] + d[v] - d[lca(u , v)]的最小值,最后再减去d[x]就ok了。复杂度O(mlogn),代码如下:
感觉代码都是板子,就不写注释了吧。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 4010 , maxm = 200010;
int n , m;
ll inf;
struct edge
{
int u , v , next;
ll w;
}E[2 * maxn] , S[maxm];
int len , head[maxn];
void add(int u , int v , ll w)
{
E[len].u = u , E[len].v = v , E[len].w = w , E[len].next = head[u];
head[u] = len++;
}
inline char get_char()
{
static char buf[1000000] , *p1 = buf , *p2 = buf;
if (p1 == p2)
{
p2 = (p1 = buf) + fread(buf , 1 , 1000000 , stdin);
if (p1 == p2)
{
return EOF;
}
}
return *p1++;
}
inline ll read()
{
ll res;
char ch;
ll f = 1;
while (!isdigit(ch = get_char()))
{
if (ch == '-')
{
f = -1;
}
}
res = ch - '0';
while (isdigit(ch = get_char()))
{
res = res * 10 + ch - '0';
}
return res;
}
int fa[14][maxn] , son[maxn] , top[maxn] , sz[maxn] , dep[maxn] , tid[maxn];
ll d[maxn];
void dfs1(int u)
{
sz[u] = 1;
for (int i = head[u]; ~i; i = E[i].next)
{
int v = E[i].v;
ll w = E[i].w;
if (v != fa[0][u])
{
dep[v] = dep[u] + 1;
fa[0][v] = u;
d[v] = d[u] + w;
dfs1(v);
sz[u] += sz[v];
if (son[u] == -1 || sz[v] > sz[son[u]])
{
son[u] = v;
}
}
}
}
int cnt = 0;
void dfs2(int u , int Top)
{
top[u] = Top;
tid[u] = ++cnt;
if (son[u] != -1)
{
dfs2(son[u] , Top);
}
for (int i = head[u]; ~i; i = E[i].next)
{
int v = E[i].v;
if (v != fa[0][u] && v != son[u])
{
dfs2(v , v);
}
}
}
int lca(int x , int y)
{
if (dep[x] < dep[y])
{
swap(x , y);
}
int i , j;
for (i = 0; (1 << i) <= n; i++)
{
;
}
i--;
for (j = i; j >= 0; j--)
{
if (dep[x] - (1 << j) >= dep[y])
{
x = fa[j][x];
}
}
if (x == y)
{
return x;
}
for (j = i; j >= 0; j--)
{
if (fa[j][x] != fa[j][y])
{
x = fa[j][x];
y = fa[j][y];
}
}
return fa[0][x];
}
ll tree[4 * maxn] , lazy[4 * maxn];
void pushdown(int id)
{
if (lazy[id] != inf)
{
lazy[id << 1] = min(lazy[id << 1] , lazy[id]);
lazy[id << 1 | 1] = min(lazy[id << 1 | 1] , lazy[id]);
tree[id << 1] = min(tree[id << 1] , lazy[id]);
tree[id << 1 | 1] = min(tree[id << 1 | 1] , lazy[id]);
lazy[id] = inf;
}
}
void update(int id , int l , int r , int x , int y , ll val)
{
if (l >= x && r <= y)
{
tree[id] = min(tree[id] , val);
lazy[id] = min(lazy[id] , val);
return;
}
int mid = (l + r) >> 1;
pushdown(id);
if (x <= mid)
{
update(id << 1 , l , mid , x , y , val);
}
if (y > mid)
{
update(id << 1 | 1 , mid + 1 , r , x , y , val);
}
tree[id] = min(tree[id << 1] , tree[id << 1 | 1]);
}
void update_path(int x , int y , int t , ll val)
{
int flag = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
{
swap(x , y);
}
update(1 , 1 , n , tid[top[x]] , tid[x] , val);
x = fa[0][top[x]];
}
if (dep[x] > dep[y])
{
swap(x , y);
}
update(1 , 1 , n , tid[son[x]], tid[y] , val);
}
ll query(int id , int l , int r , int pos)
{
if (l == r && l == pos)
{
return tree[id];
}
int mid = (l + r) >> 1;
pushdown(id);
ll res = inf;
if (mid >= pos)
{
res = min(res , query(id << 1 , l , mid , pos));
}
else
{
res = min(res , query(id << 1 | 1 , mid + 1 , r , pos));
}
return res;
}
int main()
{
memset(head , -1 , sizeof(head));
memset(son , -1 , sizeof(son));
memset(tree , 0x7f , sizeof(tree));
memset(lazy , 0x7f , sizeof(lazy));
inf = lazy[0];
n = read() , m = read();
int num = 0;
for (int i = 1; i <= m; i++)
{
int u , v , judge;
ll w;
u = read() , v = read() , w = read() , judge = read();
if (judge == 1)
{
add(u , v , w);
add(v , u , w);
}
else
{
S[++num].u = u , S[num].v = v , S[num].w = w;
}
}
dfs1(1);
dfs2(1 , 1);
for (int level = 1; (1 << level) <= n; level++)
{
for (int i = 1; i <= n; i++)
{
fa[level][i] = fa[level - 1][fa[level - 1][i]];
}
}
for (int i = 1; i <= num; i++)
{
int u = S[i].u , v = S[i].v;
ll w = S[i].w;
int t = lca(u , v);
ll tmp = d[u] + w + d[v];
update_path(u , v , t , tmp);
}
for (int i = 2; i <= n; i++)
{
ll ans = query(1 , 1 , n , tid[i]);
if (ans != inf)
{
printf("%lld\n" , ans - d[i]);
}
else
{
printf("-1\n");
}
}
return 0;
}
看完了别忘了点个赞再走哦。