CF1650G 『Counting Shortcuts』 题解
从洛谷博客那里搬过来的(
图论专题本来打算先挑最简单的做,结果做了两个多小时(
题意就是让你找从起点 到终点 的最短路以及次短路个数,本题次短路长度指的是最短路长度 。
考虑 。
设 为当前到了点 , 是与 到 的最短路长度相差 的路径的条数。
手模几组样例容易得出转移方程。先跑一遍 算出 到所有点的最短路 ,设当前点为 ,下一个点为 。则有:
-
若 ,则说明 的最短路并非 的最短路 ,而是 的最短路和 的最短路差 ,则可 。
-
若 ,则说明 的最短路是可以由 转移过来的。于是v继承u的所有状态。即:。
初态是 ,因为题目问的是最短路与次短路个数之和所以末态为 。
至此就可以高高兴兴写出一个 代码了。
void dfs(int x) {
if (x == hd)
return ;
vis[x] = true;
for (re i = head[x] ; i ; i = e[i].nxt) {
int v = e[i].v;
if (vis[v] == true)
continue;
if (dis[v] == dis[x])
Plus(dp[v][1], dp[x][0]);
else if (dis[v] == dis[x] + 1)
Plus(dp[v][0], dp[x][0]), Plus(dp[v][1], dp[x][1]);
dfs(v);
}
vis[x] = false;
}
然后测一下前三个样例,哇,都过了。以为这题就要切了,测第四个样例结果发现输出 1204
。
显然是转移多了。考虑下面一张图:
按照写的 模一下发现点 和点 转移到点 的时候出现了问题:转移顺序。
比如转移顺序:,但是还有 ,发现在第一遍转移的时候已经转移给了点 ,但是第二次转移的时候除了把新的值转移给了 ,还把以前的值又转移了一遍。所以就转移多了。
考虑如何解决。既然你是重复转移了前一次的,那我对于每个点转移过了之后把他的 值清空,下一次再来到这个点的时候就不会重复转移上一次的值了。
void dfs(int x) {
if (x == hd)
return ;
vis[x] = true;
for (re i = head[x] ; i ; i = e[i].nxt) {
int v = e[i].v;
if (vis[v] == true)
continue;
if (dis[v] == dis[x])
Plus(dp[v][1], dp[x][0]);
else if (dis[v] == dis[x] + 1)
Plus(dp[v][0], dp[x][0]), Plus(dp[v][1], dp[x][1]);
dfs(v);
}
dp[x][0] = dp[x][1] = 0;// 多加了这一句
vis[x] = false;
}
交上去会发现 TLE on test #3
,我不太清楚 常数大还是咋地,卡了半天常也过不去(
:加个记搜大概能过, 慢是因为有重复的转移,旁边的大佬说造个完全图可以卡到指数级别(。
考虑换一种解决方法。发现重复转移的实质就是拿还没转移完毕的去更新其他的了,于是考虑如何让他转移完毕再去更新。
注意到点 所有的转移都是对于 或 的 去转移,这启示我们按照 对原图进行分层。对于每个点 先对于同层也就是 的点 进行转移,全部转移完毕后再去转移 的点 即可。
转移时间复杂度是 的,最短路时间复杂度是 的,于是总时间复杂度为 。
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#define GMY (520 & 1314)
#define char_phi int
#define re register int
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout)
#define N 2000005
#define M 2000005
#define P 1000000007
using namespace std;
inline void Fastio_setup() { ios :: sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL); }
inline int MAX(int x, int y) { return ((x > y) ? (x) : (y)); }
inline int MIN(int x, int y) { return ((x < y) ? (x) : (y)); }
int n, m, star_cnt, yd, hd;
char vis[N];
int head[N], q[M<<3], dis[N];
int dp[N][2];
struct star { int v, nxt; };
struct Node {
int x, d;
friend bool operator < (Node A, Node B) { return A.d > B.d; }
};
struct star e[M<<1];
priority_queue<Node> heap;
vector<int> vec[N];
inline void star_add(int u, int v) { e[++ star_cnt].v=v, e[star_cnt].nxt=head[u], head[u]=star_cnt; }
inline void Clean() {
star_cnt = 0; vec[0].clear();
for (re i = 1 ; i <= n ; ++ i) {
head[i] = dp[i][0] = dp[i][1] = 0;
vec[i].clear();
}
}
inline void Dijkstra() {
int x = yd; for (re i = 1 ; i <= n ; ++ i) dis[i] = 0x3f3f3f3f, vis[i] = false;
heap.push( (Node) {x, 0} ); dis[x] = 0;
while (heap.empty() == false) {
x = heap.top().x; heap.pop();
if (vis[x] == true)
continue;
vis[x] = true;
for (re i = head[x] ; i ; i = e[i].nxt) {
int v = e[i].v;
if (dis[v] > dis[x] + 1) {
dis[v] = dis[x] + 1;
if (vis[v] == false)
heap.push( (Node) { v, dis[v] } );
}
}
}
}
inline void Plus(int& who, int val) { who += val; if (who >= P) who -= P; }
inline void Search() {// 先更新0再更新1
for (re i = 1 ; i <= n ; ++ i) {
vis[i] = false;
vec[dis[i]].emplace_back(i);
}
dp[yd][0] = 1;
for (re dep = 0 ; dep <= n ; ++ dep) {
for (auto x : vec[dep])
for (re i = head[x] ; i ; i = e[i].nxt) {// 先转移同层的
int v = e[i].v;
if (dis[v] == dep)
Plus(dp[v][1], dp[x][0]);
}
for (auto x : vec[dep])
for (re i = head[x] ; i ; i = e[i].nxt) {// 再转移其他层的
int v = e[i].v;
if (dis[v] == dep + 1)
Plus(dp[v][0], dp[x][0]), Plus(dp[v][1], dp[x][1]);
}
}
}
inline void work() {
Clean();
cin >> n >> m; cin >> yd >> hd;
for (re i = 1, uu, vv ; i <= m ; ++ i)
{cin >> uu >> vv; star_add(uu, vv), star_add(vv, uu);}
Dijkstra();
Search();// 也许算是bfs罢
cout << (dp[hd][0] + dp[hd][1]) % P << '\n';
}
#undef int
// #define IXINGMY
char_phi main() {
#ifdef IXINGMY
FBI_OPENTHEDOOR(a, a);
#endif
Fastio_setup();
int T; cin >> T;
while (T --)
work();
return GMY;
}
:参考了 大佬的思路。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现