不能直接跑dij的理由是取模后会丢失大小关系。
但是发现当翻转次数大于等于18后,翻转次数小的一定优于翻转次数多的。
当翻转次数小于18后直接dis[i][j]表示翻转次数是j到达i的最短路,按照遍历边的次数的大小跑dij就行了。
当翻转次数大于等于18后直接dis[i][0/1]表示翻转次数是奇数和偶数到达i的最小翻转次数及对应的最短路,跑dij就行了。不过需要注意的是,翻转次数小的一定优于翻转次数多的,按照这个标准去重载运算符。
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << #x << " = " << x << '\n'
#define ddebug(x , y) cerr << #x << " = " << x << " " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
int n , m ;
vector<pii> g[maxn] ;
int pre[maxn] ;
struct node
{
int u ;
int rev ;
int cnt ;
bool operator < (const node & s) const
{
if(rev < 20 && s.rev >= 20) return 0 ;
else if(rev >= 20 && s.rev < 20) return 1 ;
else if(rev < 20 && s.rev < 20) return pre[rev] + cnt > pre[s.rev] + s.cnt ;
else
{
if(rev != s.rev) return rev > s.rev ;
else return cnt > s.cnt ;
}
}
} ;
bool le(pii a , pii b)
{
int rev1 = a.fi ;
int cnt1 = a.se ;
int rev2 = b.fi ;
int cnt2 = b.se ;
if(rev1 != rev2) return rev1 < rev2 ;
else return cnt1 < cnt2 ;
// if(rev1 < 20 && rev2 >= 20) return 1 ;
// else if(rev1 >= 20 && rev2 < 20) return 0 ;
// else if(rev1 < 20 && rev2 < 20) return pre[rev1] + cnt1 < pre[rev2] + cnt2 ;
// else
// {
// if(rev1 != rev2) return rev1 < rev2 ;
// else return cnt1 < cnt2 ;
// }
}
struct Dij
{
int dis[maxn][20] ;
priority_queue<node> q ;
void init()
{
mem_inf(dis) ;
}
ll dijkstra(int s)
{
dis[s][0] = 0 ;
node c ;
c.u = s ;
c.rev = 0 ;
c.cnt = 0 ;
q.push(c) ;
while(!q.empty())
{
node now = q.top() ;
q.pop() ;
int u = now.u ;
//if(p.first != dis[u]) continue ; //优化,不用旧值更新。
for(auto x : g[u])
{
int v = x.fi ;
node nxt ;
nxt.u = v ;
nxt.rev = now.rev + (now.rev % 2 != x.se) ;
nxt.cnt = now.cnt + 1 ;
if(nxt.rev < 20 && nxt.cnt < dis[v][nxt.rev])
{
dis[v][nxt.rev] = nxt.cnt ;
q.push(nxt) ;
}
}
}
ll mn = inf ;
rep(i , 0 , 19) mn = min(mn , 1ll * dis[n][i] + pre[i]) ;
if(mn == inf) return -1 ;
else return mn % mod ;
}
ll solve()
{
init() ;
return dijkstra(1) ;
}
} dij ;
struct Dij2
{
pii dis[maxn][2] ;
priority_queue<node> q ;
void init()
{
rep(i , 1 , n) dis[i][0] = dis[i][1] = {2e5 , 0} ;
}
ll dijkstra(int s)
{
dis[s][0] = {0 , 0} ;
node c ;
c.u = s ;
c.rev = 0 ;
c.cnt = 0 ;
q.push(c) ;
while(!q.empty())
{
node now = q.top() ;
q.pop() ;
int u = now.u ;
//debug(u) ;
//if(p.first != dis[u]) continue ; //优化,不用旧值更新。
for(auto x : g[u])
{
int v = x.fi ;
//debug(v) ;
node nxt ;
nxt.u = v ;
nxt.rev = now.rev + (now.rev % 2 != x.se) ;
nxt.cnt = now.cnt + 1 ;
if(le({nxt.rev , nxt.cnt} , dis[v][nxt.rev % 2]))
{
dis[v][nxt.rev % 2] = {nxt.rev , nxt.cnt} ;
q.push(nxt) ;
}
}
}
//debug(dis[n][0].fi) ;
if(dis[n][0].fi < dis[n][1].fi) return (pre[dis[n][0].fi] + dis[n][0].se) % mod ;
else return (pre[dis[n][1].fi] + dis[n][1].se) % mod ;
}
ll solve2()
{
init() ;
return dijkstra(1) ;
}
} dij2 ;
int main()
{
ios ;
cin >> n >> m ;
rep(i , 1 , m)
{
int u , v ;
cin >> u >> v ;
g[u].pb({v , 0}) ;
g[v].pb({u , 1}) ;
}
pre[1] = 1ll ;
rep(i , 2 , 200000) pre[i] = pre[i - 1] * 2ll , pre[i] %= mod ;
rep(i , 2 , 200000) pre[i] += pre[i - 1] , pre[i] %= mod ;
ll ans = dij.solve() ;
if(ans == -1) ans = dij2.solve2() ;
cout << ans << '\n' ;
return 0 ;
}