Codeforces Round #681 (Div. 1, based on VK Cup 2019-2020 - Final) C. Graph Transpositions

我们可以分两种情况讨论
1.如果原图存在一条1到n的有向通路。那么现在就有一个1到n的最大值x,我们可以通过转置图让x变小,但是不可能转置次数超过log(x+1)次
所以我们可以建20层分层图,每一次间的值就是2^(k-1)+1,然后跑最短路就可以了,然后每一次n点中的最小值就是答案
2.如果发现没办法到n,那么说明原图中不存在一条1到n的通路
那么我们假设进行了x次图转置,走的路径长度是y,那么我们肯定首先要保证x最小,因为y是肯定小于x的,在x相等的情况下,就要找y最小
然后我们建两层的分层图再按{x,y}跑最短路就好了

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<set>
#include<map>
#include<string>
#include<string.h>
#define maxn 5005000
#define ll long long
#include<vector>
#include<queue>
using namespace std;
const ll mod=998244353;
const ll inf=1e18;
ll qpow(ll x,ll n)
{
    ll ans=1;
    while(n)
    {
        if(n&1) ans*=x,ans%=mod;
        x*=x;x%=mod;
        n/=2;
    }
    return ans;
}
struct node
{
    ll to,w;
    friend bool operator<(node a,node b)
    {
        return a.w>b.w;
    }
};
ll dis[maxn],vis[maxn];
ll head[maxn],cnt;
struct E
{
    ll to,w,next;
}e[maxn*2];
void add(ll u,ll v,ll w)
{
    e[cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
struct nd
{
    ll x,y;
}edge[1050000];
priority_queue<node>q;
ll n,m;
void dijk(ll st)
{
    for(ll i=0;i<=22*n+100;i++) vis[i]=0,dis[i]=inf;
    dis[st]=0;
    q.push({st,0});

    while(!q.empty())
    {
        ll u=q.top().to;q.pop();
        if(vis[u]==1) continue;
        vis[u]=1;
        // printf("dis[%lld]=%lld\n",u,dis[u]);
        for(ll i=head[u];i!=-1;i=e[i].next)
        {
            ll v=e[i].to,w=e[i].w;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                //printf("  dis[%lld]=%lld w=%lld\n",v,dis[v],w);
                q.push({v,dis[v]});
            }
        }
    }
}
#define maxn1 1050000
struct nd1
{
    ll x,y;
    friend bool operator<(nd1 a,nd1 b)
    {
        if(a.x==b.x) return a.y>b.y;
        else return a.x>b.x;
    }
}dis1[maxn1];
struct node1
{
    ll to,x,y;
    friend bool operator<(node1 a,node1 b)
    {
        if(a.x==b.x) return a.y>b.y;
        else return a.x>b.x;
    }
};
priority_queue<node1>q1;
vector<node1>e1[maxn1];
ll vis1[maxn1];
void dijk1(ll st)
{
    for(ll i=0;i<=n*3+100;i++) dis1[i]={inf,inf},vis1[i]=0;
    vis1[st]=0;dis1[st]={0,0};
    q1.push({st,0,0});
    while(!q1.empty())
    {
        //printf("1111\n");
        ll u=q1.top().to;q1.pop();
        if(vis1[u]==1) continue;
        vis1[u]=1;
        //printf("u=%lld x=%lld y=%lld\n",u,dis1[u].x,dis1[u].y);
        for(node1 i:e1[u])
        {
            ll v=i.to,x=i.x,y=i.y;
            if(dis1[u].x+x<dis1[v].x||((dis1[u].x+x==dis1[v].x)&&(dis1[u].y+y<dis1[v].y)))
            {
                dis1[v]={dis1[u].x+x,dis1[u].y+y};
                //printf("  v=%lld x=%lld y=%lld\n",v,dis1[v].x,dis1[v].y);
                q1.push({v,dis1[v].x,dis1[v].y});
            }
        }
    }
}
int main()
{

    memset(head,-1,sizeof(head));cnt=0;
    scanf("%lld %lld",&n,&m);
    for(ll i=1;i<=m;i++)
    {
        ll u,v;
        scanf("%lld %lld",&u,&v);
        edge[i]={u,v};
        for(ll j=1;j<=20;j++)
        {
            if(j%2==1)
            {
                add(n*(j-1)+u,n*(j-1)+v,1);
                if(j!=20)add(n*(j-1)+v,n*j+u,(1ll<<(j-1))+1);
            }
            else
            {
                add(n*(j-1)+v,n*(j-1)+u,1);
                if(j!=20)add(n*(j-1)+u,n*j+v,(1ll<<(j-1))+1);
            }
        }
    }
    dijk(1);
    ll ans=inf;
    for(ll i=n;i<=20*n;i+=n) ans=min(ans,dis[i]);
    if(ans!=inf)
    {
        printf("%lld\n",ans);
        return 0;
    }
    for(ll i=1;i<=m;i++)
    {
        ll u=edge[i].x,v=edge[i].y;
        e1[u].push_back({v,0,1});
        e1[v].push_back({n+u,1,1});
        e1[n+v].push_back({n+u,0,1});
        e1[n+u].push_back({v,1,1});
    }
    /*for(ll i=1;i<=2*n;i++)
    {
        printf("u=%lld\n",i);
        for(node1 j:e1[i]) printf("%lld ",j.to);
        printf("\n");
    }*/
    dijk1(1);
    nd1 ans1;
    if(dis1[n].x<dis1[2*n].x) ans1=dis1[n];
    else if(dis1[n].x>dis1[2*n].x) ans1=dis1[2*n];
    else if(dis1[n].y<dis1[2*n].y) ans1=dis1[n];
    else ans1=dis1[2*n];
    /*if(dis1[n]<dis1[2*n]) ans1=dis1[n];
    else ans1=dis1[2*n];
    printf("dis1[n].x=%lld dis1[n].y=%lld\n",dis1[n].x,dis1[n].y);
    printf("dis1[2n].x=%lld dis1[2n].y=%lld\n",dis1[2*n].x,dis1[2*n].y);
    printf("%lld %lld\n",ans1.x,ans1.y);*/
    printf("%lld\n",(qpow(2,ans1.x)-1+ans1.y+mod)%mod);
    return 0;
}
/*
23 22
2 1
2 3
4 3
4 5
6 5
6 7
8 7
8 9
10 9
10 11
12 11
12 13
14 13
14 15
16 15
16 17
18 17
18 19
20 19
20 21
22 21
22 23
*/

上一篇:事件和概率


下一篇:java并发编程之美——基础篇