• 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)分的最短路计数打出来

  • 相关阅读:
    Haskell语言学习笔记(54)Data.Set
    Haskell语言学习笔记(53)Data.Sequence
    正则表达式(Java,C#,C++)
    Haskell语言学习笔记(52)正则表达式
    Haskell语言学习笔记(51)Comonad
    最大获利
    最小生成树
    PIGS
    三维偏序
    <noip2017>列队
  • 原文地址:https://www.cnblogs.com/xunzhen/p/9788111.html
Copyright © 2020-2023  润新知