codeforces 1442C Graph Transpositions

不能直接跑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 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值