>Link
luogu P4568
>Description
给出一张 n n n 个点, m m m 条边的无向图。选择其中的一条路,可以最多减去其中 k k k 条路径的权值
求 S S S 到 T T T 的最短路
n ≤ 1 0 4 , m ≤ 5 ∗ 1 0 4 , k ≤ 10 n\le 10^4,m\le5*10^4,k\le10 n≤104,m≤5∗104,k≤10
>解题思路
我们把点复制成 k + 1 k+1 k+1 层,其中跨去另一层的次数为 k k k,可以在这之间搞减去的路径的权值
把相邻两层之间的边权值赋为 0,每层之间正常建边,这样从一层走到另一层就相当于把这条路径的权值减去了
最终答案就是第 S S S 层的 1 1 1 到第 k k k 层的 T T T 的最短路
SPFA会被卡,这里用到dij+堆优化 SPFA已经死了 (虽然但是我之前一直在用SPFA)
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 800010
using namespace std;
struct node
{
int id, ds;
friend bool operator < (node aa, node bb)
{
return aa.ds > bb.ds;
}
};
priority_queue<node> Q;
struct edge
{
int to, nxt, w;
} e[8000000];
int n, m, k, S, T, cnt, h[N], dis[N], ans;
bool vis[N];
void add (int u, int v, int w)
{
e[++cnt] = (edge){
v, h[u], w};
h[u] = cnt;
}
int main()
{
// printf ("%.3lf", (double) sizeof (vis) / 1024 / 1024);
scanf ("%d%d%d%d%d", &n, &m, &k, &S, &T);
S++, T++;
int u, v, w, l;
for (int i = 1; i <= m; i++)
{
scanf ("%d%d%d", &u, &v, &w);
u++, v++;
for (int j = 1; j <= k + 1; j++)
{
l = (j - 1) * n;
add (l + u, l + v, w);
add (l + v, l + u, w);
if (j > 1)
{
add (l - n + u, l + v, 0);
add (l - n + v, l + u, 0);
}
}
}
memset (dis, 0x7f, sizeof (dis));
ans = dis[0];
Q.push ((node){
S, 0}); dis[S] = 0;
while (!Q.empty())
{
node u = Q.top();
Q.pop();
if (vis[u.id]) continue;
vis[u.id] = 1;
for (int i = h[u.id]; i; i = e[i].nxt)
{
v = e[i].to;
if (!vis[v] && dis[u.id] + e[i].w < dis[v])
{
dis[v] = dis[u.id] + e[i].w;
Q.push ((node){
v, dis[v]});
}
}
}
for (int i = 1; i <= k + 1; i++)
ans = min (ans, dis[(i - 1) * n + T]);
printf ("%d", ans);
return 0;
}