题目大意:有一个边权为1的有向图,可以使用魔法将所有的边转向,第 i i i 次使用魔法要花费 2 i − 1 2^{i-1} 2i−1 单位时间,求从 1 号点到 n n n 号点需要花费的最少时间,答案对 998244353 998244353 998244353 取模。
题解:魔法最多需要使用 n − 1 n - 1 n−1 次,但当魔法使用超过18次时,由于 2 18 > 200000 2^{18} > 200000 218>200000,使用魔法次数更小的情况一定比使用魔法次数更多的情况更优。
对于魔法使用次数不超过18次的情况,答案也不会超过模数,令 d [ i ] [ j ] d[i][j] d[i][j] 表示从 1 1 1 到 i i i 使用了 j j j 次魔法的最短时间,可以暴力跑 d i j k s t r a dijkstra dijkstra 算法。
对于魔法使用次数超过18次的情况,答案会超过模数不能直接跑 d i j k s t r a dijkstra dijkstra,但我们只需要比较使用魔法的次数,在次数相同的情况下比较走过的边数。分别记录从1号到每个点走过的边数和使用的魔法次数,分奇偶进行转移。
注意:本题卡了 s p f a spfa spfa 算法
时间复杂度 O ( 20 n log m ) O(20 n\log m) O(20nlogm)
代码:
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
const int mod = 998244353;
const int maxn = 2e5 + 10;
int n, m, res;
vector<pii> g[maxn];
int dp[maxn][22], vis[maxn][22];
int d[maxn][2], t[maxn][2];
int p[maxn];
struct ss {
int u, i, v;
ss () {}
ss (int ui,int j,int vi) {
u = ui;
i = j;
v = vi;
}
bool operator < (const ss &rhs) const {
return v > rhs.v;
}
};
struct node {
int u, i, v, c;
node() {}
node(int ui,int j,int vi,int ci) {
u = ui;
i = j;
v = vi;
c = ci;
}
bool operator < (const node &rhs) const {
if (c != rhs.c) return c > rhs.c;
return v > rhs.v;
}
};
void dijkstra1() {
for (int i = 1; i <= n; i++)
for (int j = 0; j <= 18; j++)
dp[i][j] = mod, vis[i][j] = 0;
priority_queue<ss> q;
for (int i = 0; i <= 18; i++) {
dp[1][i] = (1 << i) - 1;
q.push(ss(1,i,dp[1][i]));
}
while (!q.empty()) {
ss top = q.top(); q.pop();
int u = top.u, i = top.i;
if (vis[u][i]) continue;
vis[u][i] = 1;
for (auto it : g[u]) {
int v = it.fir, c = it.sec;
if (i % 2 != c) {
if (i < 18 && dp[v][i + 1] > dp[u][i] + 1 + (1 << i)) {
dp[v][i + 1] = dp[u][i] + 1 + (1 << i);
q.push(ss(v,i + 1,dp[v][i + 1]));
}
} else {
if (dp[v][i] > dp[u][i] + 1) {
dp[v][i] = dp[u][i] + 1;
q.push(ss(v,i,dp[v][i]));
}
}
}
}
}
void dijkstra2() {
priority_queue<node> q;
for (int i = 1; i <= n; i++) {
d[i][0] = d[i][1] = mod;
t[i][0] = mod; t[i][1] = mod;
vis[i][0] = vis[i][1] = 0;
}
for (int i = 1; i <= n; i++) {
if (dp[i][18] != mod) {
d[i][0] = dp[i][18] - (1 << 18) + 1;
t[i][0] = 18;
q.push(node(i,0,d[i][0],t[i][0]));
}
}
while (!q.empty()) {
node top = q.top(); q.pop();
int u = top.u, i = top.i;
if (vis[u][i]) continue;
vis[u][i] = 1;
for (auto it : g[u]) {
int v = it.fir, c = it.sec;
if (i == c) { //不改变奇偶
if (t[v][i] > t[u][i] || t[v][i] == t[u][i] && d[v][i] > d[u][i] + 1) {
t[v][i] = t[u][i];
d[v][i] = d[u][i] + 1;
q.push(node(v,i,d[v][i],t[v][i]));
}
} else {
if (t[v][i ^ 1] > t[u][i] + 1 || t[v][i ^ 1] == t[u][i] + 1 && d[v][i ^ 1] > d[u][i] + 1) {
t[v][i ^ 1] = t[u][i] + 1;
d[v][i ^ 1] = d[u][i] + 1;
q.push(node(v,i ^ 1,d[v][i ^ 1],t[v][i ^ 1]));
}
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
p[0] = 1;
for (int i = 1; i <= 200000; i++)
p[i] = p[i - 1] * 2 % mod;
for (int i = 1; i <= m; i++) {
int u, v; scanf("%d%d",&u,&v);
g[u].push_back(pii(v,0));
g[v].push_back(pii(u,1));
}
dijkstra1();
dijkstra2();
int res = mod;
for (int i = 0; i <= 18; i++)
res = min(res,dp[n][i]);
if (res != mod) printf("%d\n",res);
else {
if (t[n][0] < t[n][1]) {
printf("%d\n",(1ll * p[t[n][0]] - 1 + d[n][0] + mod) % mod);
} else {
printf("%d\n",(1ll * p[t[n][1]] - 1 + d[n][1] + mod) % mod);
}
}
return 0;
}