A*

不会。O(nklogk)

最短路径树+可并堆+优先队列

首先求所有点到T的最短路dis,构建出最短路径树。
转化:一条ST的路径可以双射为路径上的非树边序列
正确性: 每条路径唯一对应……(显然)
每个合法序列相邻非树边之间连接的树边顺序唯一,所以也唯一对应嘛。
而且路径长度是:dis(S)加上所有非树边uv贡献dis(v)+lendis(u)。证明很显然。

当然这个序列是有限制的。因为最短路径树的树边都是往上父亲指的。所以说相邻两个非树边,后面的开始点是前面的结尾点的祖先
找前k大,最短路可求大,每次找排名下一位的路径。
存在一种序列的构造方法:
1.修改:将最后一条边改成最小的不小于它的边。
2.添加:加一条尽量小的边,新边开始点满足:在结尾点的祖先的子树中。
用可并堆维护每个节点为起点的边(以边权为关键字从小到大),要查询所有祖先的信息,所以用可持久化可并堆每个节点存入所有祖先的信息,也就是在父亲的基础上加入自己的即可。

可以用优先队列模拟这两条。队列里维护当前路径长度,以及所在可并堆节点。
每次取出最优的,去构造其它的。
1.修改的边要和该边同样范围内,因此就是在该可并堆中第一个比它大的,也就是把左右儿子加入优先队列中。
2.查询当前边结束点的祖先信息,即把结束点可并堆的堆顶节点加入。

  • code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef double db;
const int N = 1e6 + 5;
const int M = 6e6 + 5;
const db eps = 1e-9;
const db inf = 1000000000000000000;
int n, m, fa[N];
db E, dis[N];
struct edge {
	int head[N], to[N], nxt[N], ecnt;
	db len[N];
	void add_edge(int u, int v, db w) {nxt[++ecnt] = head[u]; to[ecnt] = v; len[ecnt] = w; head[u] = ecnt;};
}Z, F;

int nd, rt[N];
struct Heap {
	int ls, rs, d0, ed;
	db w;
}H[M];

int _new(int ed, db w) {
++nd; H[nd].ed = ed; H[nd].w = w; return nd;}

int Merge(int u, int v) {
	if(!u || !v) return u + v;
	if(H[u].w > H[v].w) swap(u, v);
	int nw = ++nd;
	H[nw] = H[u];
	H[nw].ls = Merge(H[nw].ls, v);
	if(H[H[nw].ls].d0 > H[H[nw].rs].d0) swap(H[nw].ls, H[nw].rs);
	H[nw].d0 = H[H[nw].ls].d0 + 1;
	return nw;
}

struct pq {
	int p; db w;
	bool operator<(const pq &u) const{return w - u.w > eps;}
};
priority_queue<pq> Q;
queue<int> P;
bool In_s[N];
void spfa(int s) {
	for(int i = 1; i <= n; i++) dis[i] = inf;
	dis[s] = 0; P.push(s); 
	while(!P.empty()) {
		int u = P.front(); P.pop();
		In_s[u] = 0;
		for(int i = F.head[u]; i; i = F.nxt[i]) {
			int v = F.to[i];
			if(dis[v] - (dis[u] + F.len[i]) > eps) {
				dis[v] = dis[u] + F.len[i];
				fa[v] = i;
				if(!In_s[v]) In_s[v] = 1, P.push(v);
			}
		}
	}
}

int pos[N];
void Build() {		//最短路径树+左偏树维护挂在到 T 路径的子树内的边 
	for(int i = 1; i <= n; i++) {pos[i] = i;}
	sort(pos + 1, pos + 1 + n, [&](int u, int v) {return dis[v] - dis[u] > eps;});
	for(int i = 1; i <= n; i++) {
		int u = pos[i], f = Z.to[fa[u]];
		for(int j = Z.head[u]; j; j = Z.nxt[j]) {
			int v = Z.to[j];
			if(j == fa[u]) continue;
			rt[u] = Merge(rt[u], _new(v, dis[v] + Z.len[j] - dis[u]));		//非树边 
		}
		rt[u] = Merge(rt[u], rt[f]);
	}
}

void solve() {
	int ans = 0;
	if(dis[1] - E > eps) {puts("0"); return;}
	E -= dis[1]; ans++;
	if(rt[1]) Q.push((pq){rt[1], dis[1] + H[rt[1]].w});
	while(!Q.empty()) {
		int u = Q.top().p; db w = Q.top().w; Q.pop();
		if(w - E > eps) {break;}
		E -= w; ans++;
		int ed = H[u].ed, ls = H[u].ls, rs = H[u].rs;
		//change
		if(ls) Q.push((pq){ls, w - H[u].w + H[ls].w});
		if(rs) Q.push((pq){rs, w - H[u].w + H[rs].w});
		//add
		if(rt[ed]) Q.push((pq){rt[ed], w + H[rt[ed]].w});
	}
	printf("%d\n", ans);
}

int main() {
	scanf("%d%d%lf", &n, &m, &E);
	for(int i = 1; i <= m; i++) {
		int u, v; db w; scanf("%d%d%lf", &u, &v, &w);
		if(u == n) continue;
		Z.add_edge(u, v, w); F.add_edge(v, u, w);
	}
	spfa(n);
	Build();
	solve();
	return 0;
}