Luogu3953 [NOIP2017]逛公园

题目蓝链

Solution

我们设(dp[i][j])表示到第(i)个点多走了(j)步的方案数,(dis[i])表示从(1)(i)的最短距离

显然有以下转移方程式,

[dp[i][j] = sum dp[k][dis_i + j - len_{i, k} - dis_k] ]

其中,(k)为所有(i)可以直接到达的节点

最后的那个式子表示的是在(k)这个节点的时候多走的步数,使得再走(len_{i, k})步之后恰好多走(j)

至于怎么判(0)环,就只要在搜索的时候打一下(vis)标记,看一下有没有搜出环来就可以了

Code

#include <bits/stdc++.h>

using namespace std;

#define fst first
#define snd second
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

inline int read() {
	int sum = 0, fg = 1; char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
	return fg * sum;
}

const int maxn = 1e5 + 10;
const int maxm = 2e5 + 10;
const int maxk = 51;

struct graph {
	int Begin[maxn], Next[maxm], To[maxm], w[maxm], e;
	void init() { e = -1, memset(Begin, -1, sizeof Begin); }
	void add(int x, int y, int z) { To[++e] = y, Next[e] = Begin[x], Begin[x] = e, w[e] = z; }
}g, _g;

int f[maxn][maxk], d[maxn], mod;
bool v[maxn], hk, vis[maxn][maxk];

inline int add(int x, int y) { return (x += y) < mod ? x : x - mod; }

void dijkstra(int s) {
	memset(v, 0, sizeof v);
	memset(d, 0x3f, sizeof d), d[s] = 0;
	priority_queue<pii, vector<pii>, greater<pii> > pq;
	pq.push((pii){0, s});
	while (!pq.empty()) {
		int now = pq.top().snd; pq.pop();
		if (v[now]) continue; v[now] = 1;
		for (int i = g.Begin[now]; i + 1; i = g.Next[i]) {
			int son = g.To[i], ds = g.w[i];
			if (d[now] + ds < d[son]) {
				d[son] = d[now] + ds;
				pq.push((pii){d[son], son});
			}
		}
	}
}

int dfs(int now, int k) {
	if (~f[now][k]) return f[now][k];
	if (now == 1 && k == 0) f[now][k] = 1;
	else f[now][k] = 0;
	vis[now][k] = 1;
	for (int i = _g.Begin[now]; i + 1; i = _g.Next[i]) {
		int son = _g.To[i], ds = _g.w[i], tmp = d[now] + k - ds - d[son];
		if (tmp < 0) continue;
		if (vis[son][tmp]) hk = 1;
		f[now][k] = add(f[now][k], dfs(son, tmp));
	}
	vis[now][k] = 0;
	return f[now][k];
}

int main() {
#ifdef xunzhen
	freopen("park.in", "r", stdin);
	freopen("park.out", "w", stdout);
#endif

	int T = read();
	while (T--) {
		g.init(), _g.init();
		int n = read(), m = read(), k = read(), ans = 0;
		mod = read();
		for (int i = 1; i <= m; i++) {
			int x = read(), y = read(), z = read();
			g.add(x, y, z), _g.add(y, x, z);
		}
		dijkstra(1); memset(f, -1, sizeof f), hk = 0;
		for (int i = 0; i <= k; i++) ans = add(ans, dfs(n, i));
		printf("%d
", hk ? -1 : ans);
	}

	return 0;
}

Summary

我去年考试的时候这题爆零,其实就算不会这个DP,也应该要把(30)分的最短路计数打出来

原文地址:https://www.cnblogs.com/xunzhen/p/9788111.html