NC14501 大吉大利,晚上吃鸡!
题目
题目描述
最近《绝地求生:大逃杀》风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏。
在游戏中,皮皮和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的快递。
当然,有些时候并不能堵桥,皮皮和毛毛会选择在其他的必经之路上蹲点。
K博士作为一个老年人,外加有心脏病,自然是不能玩这款游戏的,但是这并不能妨碍他对这款游戏进行一些理论分析,比如最近他就对皮皮和毛毛的战士很感兴趣。
游戏的地图可以抽象为一张 n 个点 m 条无向边的图,节点编号为 1 到 n ,每条边具有一个正整数的长度。
假定大魔王都会从 S 点出发到达 T 点( S 和 T 已知),并且只会走最短路,皮皮和毛毛会在 A 点和 B 点埋伏大魔王。
为了保证一定能埋伏到大魔王,同时又想留大魔王一条生路,皮皮和毛毛约定 A 点和 B 点必须满足:
\1. 大魔王所有可能路径中,必定会经过 A 点和 B 点中的任意一点
\2. 大魔王所有可能路径中,不存在一条路径同时经过 A 点和 B 点
K博士想知道,满足上面两个条件的 A,B 点对有多少个,交换 A,B 的顺序算相同的方案。
输入描述
第一行输入四个整数 n,m,S,T(),含义见题目描述。
接下来输入 m 行,每行输入三个整数 u,v,w()表示存在一条长度为 w 的边链接 u 和 v 。
输出描述
输出一行表示答案。
示例1
输入
7 7 1 7 1 2 2 2 4 2 4 6 2 6 7 2 1 3 2 3 5 4 5 7 2
输出
6
说明
合法的方案为 <2,3>,<2,4>,<4,3>,<4,5>,<6,3>,<6,5>。
备注
题解
知识点:最短路,拓扑排序,计数dp,DAG上dp。
这道题分几步走:
- 跑正反两次最短路,得到 ,再求经过每个点 的最短路条数 ,不在最短路上的点应为 。同时,记录方案数到点的映射 方便最后统计。注意不在最短路上的点也要统计,即 ,因为两个点可以有一个点不在最短路上,而另一个点通过了所有最短路。因为方案数本身过大,所以取了模,虽然很玄学,但能过。
- 根据第一步得到的 新建一个最短路DAG图,即只包括最短路上的边且是单向的。在最短路图上跑一边拓扑排序得到拓扑序。对于每个点 ,根据拓扑序求出可以从起点开始经过哪些点到达 ,以及拓扑逆序求出经过哪些点到达终点 。这部分用
bitset
实现刚刚好。 - 统计对于每个点 满足以下两个条件的点 : 经过 到 都不可能被经过,并且 。前者保证 不出现在一条最短路上满足条件2,后者保证 不重不漏的划分了所有路径,即从 到 必定经过 满足条件1。
最后,因为交换A,B顺序算一个答案,所以统计的答案除以 。
注意,有可能 不能到达 ,即 ,此时应该A,B可以是任意点,答案是 直接输出。因为最短路图建不成,所以不能继续走后面的步骤,应该直接输出。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; template<class T> struct Graph { struct edge { int v, nxt; T w; }; int idx; vector<int> h; vector<edge> e; Graph(int n, int m) :idx(0), h(n + 1), e(m + 1) {} void init(int n) { idx = 0; h.assign(n + 1, 0); } void add(int u, int v, T w) { e[++idx] = edge{ v,h[u],w }; h[u] = idx; } }; const int N = 50007, M = 50007 << 1, mod = 1e9 + 7; Graph<int> g(N, M), g2(N, M); int n, m; vector<vector<ll>> dis(2, vector<ll>(N)); vector<vector<int>> f(2, vector<int>(N)); vector<int> ff(N); map<int, bitset<N>> mp; void dijkstra(int st, vector<ll> &dis, vector<int> &f) { dis.assign(n + 1, 0x3f3f3f3f3f3f3f3f); vector<bool> vis(n + 1, false); struct node { int v; ll w; bool operator<(const node &a)const { return w > a.w; } }; priority_queue<node> pq; dis[st] = 0; f[st] = 1; pq.push(node{ st,0 }); while (!pq.empty()) { int u = pq.top().v; pq.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = g.h[u];i;i = g.e[i].nxt) { int v = g.e[i].v, w = g.e[i].w; if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; pq.push(node{ v,dis[v] }); f[v] = f[u]; } else if (dis[v] == dis[u] + w) { f[v] = (f[v] + f[u]) % mod; } } } } vector<int> topo; void toposort() { vector<int> deg(n + 1, 0); queue<int> q; for (int i = 1;i <= g2.idx;i++) deg[g2.e[i].v]++; for (int i = 1;i <= n;i++) if (!deg[i]) q.push(i); while (!q.empty()) { int u = q.front(); topo.push_back(u); q.pop(); for (int i = g2.h[u];i;i = g2.e[i].nxt) { int v = g2.e[i].v; deg[v]--; if (!deg[v]) q.push(v); } } } bitset<N> tran[2][N]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int S, T; cin >> n >> m >> S >> T; for (int i = 1;i <= m;i++) { int u, v, w; cin >> u >> v >> w; g.add(u, v, w); g.add(v, u, w); } //分别得到S,T到各个点的最短路和方案数 dijkstra(S, dis[0], f[0]); dijkstra(T, dis[1], f[1]); if (f[0][T] == 0)//注意判断不连通的情况(等价于f[1][s]==0) { cout << 1LL * n * (n - 1) / 2 << '\n';//A可能情况*B可能情况/2(防止重复) return 0; } //在最短路上的点计算总条数,其他为0;并且统计一个方案数对应的点,方案数取模因为太大,不会被卡 for (int u = 1;u <= n;u++) { if (dis[0][u] + dis[1][u] == dis[0][T]) ff[u] = 1LL * f[0][u] * f[1][u] % mod; mp[ff[u]][u] = 1; } //用最短路上的边建图 for (int u = 1;u <= n;u++) { for (int i = g.h[u];i;i = g.e[i].nxt) { int v = g.e[i].v, w = g.e[i].w; if (dis[0][u] + dis[1][v] + w == dis[0][T]) g2.add(u, v, w); } } //得到最短路遍历顺序 toposort(); //分别获得起点传递,终点逆传递 for (int i = 0;i < n;i++) { int u = topo[i]; tran[0][u][u] = 1; for (int j = g2.h[u];j;j = g2.e[j].nxt) { int v = g2.e[j].v; tran[0][v] |= tran[0][u]; } } for (int i = n - 1;i >= 0;i--) { int u = topo[i]; tran[1][u][u] = 1; for (int j = g2.h[u];j;j = g2.e[j].nxt) { int v = g2.e[j].v; tran[1][u] |= tran[1][v]; } } //计算答案:两点不能在同一条最短路上,即g2中没有传递性;最短路至少通过一点,即两点方案数之和等于总方案数 ll ans = 0; for (int u = 1;u <= n;u++) { ans += ((~(tran[0][u] | tran[1][u])) & mp[(ff[T] - ff[u] + mod) % mod]).count(); } cout << ans / 2 << '\n'; return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17026229.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧