洛谷P3953 逛公园(dp 拓扑排序)
程序员文章站
2022-07-02 14:27:17
题意 "题目链接" Sol 去年考NOIP的时候我好像连最短路计数都不会啊qwq。。 首先不难想到一个思路,$f[i][j]$表示到第$i$个节点,与最短路之差长度为$j$的路径的方案数 首先把每个节点的最短路求出来 转移的时候按拓扑序(也就是按距离从小到大排序)转移一下 然而有$0$边的时候会挂掉 ......
题意
sol
去年考noip的时候我好像连最短路计数都不会啊qwq。。
首先不难想到一个思路,\(f[i][j]\)表示到第\(i\)个节点,与最短路之差长度为\(j\)的路径的方案数
首先把每个节点的最短路求出来
转移的时候按拓扑序(也就是按距离从小到大排序)转移一下
然而有\(0\)边的时候会挂掉,原因是会有dis相同的时候,这时候单按dis排序会无法判断转移方向
一种方案是直接把所有\(0\)边加入到新图中,拓扑排序一遍。得到第二关键字
同时判断一下\(0\)环
#include<bits/stdc++.h> #define pair pair<int, int> #define mp make_pair #define fi first #define se second using namespace std; const int maxn = 100001, inf = 1e9 + 7; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int t, n, m, k, mod, f[maxn][51], dis1[maxn], disn[maxn], vis[maxn], inder[maxn], id[maxn]; vector<pair> v[maxn], rv[maxn]; vector<pair<pair<int, int>, int> > p; vector<int> e[maxn]; void init() { memset(f, 0, sizeof(f)); memset(inder, 0, sizeof(inder)); p.clear(); f[1][0] = 1; n = read(); m = read(); k = read(); mod = read(); for(int i = 1; i <= n; i++) v[i].clear(), e[i].clear(); for(int i = 1; i <= m; i++) { int x = read(), y = read(), z = read(); v[x].push_back(mp(y, z)); rv[y].push_back(mp(x, z)); if(!z) e[x].push_back(y), inder[y]++; } } void add(int &x, int y) { if(x + y < 0) x = (x + y + mod); else x = (x + y >= mod ? x + y - mod : x + y); } int topsort() { int cnt = 0; queue<int> q; for(int i = 1; i <= n; i++) if(!inder[i]) id[i] = ++cnt, q.push(i); while(!q.empty()) { int p = q.front(); q.pop(); for(int i = 0, to; i < e[p].size(); i++) if(!(--inder[to = e[p][i]])) q.push(to), id[to] = ++cnt; } for(int i = 1; i <= n; i++) if(inder[i] && (dis1[i] + disn[i] <= dis1[n] + k)) return -1; return 0; } void dij(int bg, int *dis) { priority_queue<pair> q; q.push(mp(0, bg)); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; i++) dis[i] = inf + 1; dis[bg] = 0; while(!q.empty()) { if(vis[q.top().se]) {q.pop(); continue;} int p = q.top().se; q.pop(); vis[p] = 1; if(bg == 1) { for(int i = 0, to; i < v[p].size(); i++) if(dis[to = v[p][i].fi] > dis[p] + v[p][i].se) dis[to] = dis[p] + v[p][i].se, q.push(mp(-dis[to], to)); } else { for(int i = 0, to; i < rv[p].size(); i++) if(dis[to = rv[p][i].fi] > dis[p] + rv[p][i].se) dis[to] = dis[p] + rv[p][i].se, q.push(mp(-dis[to], to)); } } } int solve() { dij(1, dis1); dij(n, disn); if(topsort() == -1) return -1; for(int i = 1; i <= n; i++) p.push_back(mp(mp(dis1[i], id[i]), i)); sort(p.begin(), p.end()); f[1][0] = 1; for(int j = 0; j <= k; j++) { for(int i = 0; i < n; i++) { int x = p[i].se; for(int k = 0; k < v[x].size(); k++) { int to = v[x][k].fi, w = v[x][k].se, ps = dis1[x] + j + w - dis1[to]; if(ps <= k) add(f[to][ps], f[x][j]); } } } int ans = 0; for(int i = 0; i <= k; i++) add(ans, f[n][i]); return ans; } int main() { t = read(); while(t--) { init(); printf("%d\n", solve()); } return 0; } /* */
上一篇: 构造函数与原型对象
下一篇: html与css命名规范小结