JOI 2020 Final 奥运公交

URL

https://loj.ac/problem/3255

简要题意

给一张n个点m条边的有向图,有边权。
对于一条边允许花费一定代价翻转起点终点。
求至多翻转一条边后[翻转的代价]+[1走到n的代价]+[n走到1的代价]的最小值。

解法

https://codeforces.com/blog/entry/73648?#comment-579708
这个讲得很清楚。

实现

#include <bits/stdc++.h>
using namespace std;

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define bg begin()
#define ed end()
#define all(x) (x).bg,(x).ed
#define si(x) int((x).size())
#define mp make_pair
#define a first
#define b second

#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using ll=long long;
using uint=unsigned int;
using pi=pair<int,int>;
using vi=vc<int>;

int topbit(signed t){
    return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
    return t==0?-1:63-__builtin_clzll(t);
}

const int nmax=210;
const int mmax=50010;

struct E{
    int u,v,c,d;
    int other(int a){
        return a^u^v;
    }
    friend istream& operator>>(istream&i,E&e){
        i>>e.u>>e.v>>e.c>>e.d;
        e.u--,e.v--;
        return i;
    }
};

int n,m;
E es[mmax];
vi g[nmax];

int dist[nmax];
int pre[nmax];
bool mark[mmax];
vi path;

void build(){
    rep(i,n)g[i]={};
    rep(i,m)
        g[es[i].u].pb(i);
}

int comb(int a,int b){
    return (a==-1||b==-1?-1:a+b);
}

int comb(int a,int b,int c){
    return comb(a,comb(b,c));
}

void upd(int&a,int b){
    if(b!=-1&&(a==-1||a>b))a=b;
}

void sssp(){
    rep(i,n)dist[i]=-1;
    dist[0]=0;
    priority_queue<pi,vc<pi>,greater<pi>> pq;
    pq.emplace(0,0);
    while(!pq.empty()){
        int v=pq.top().second;
        int d=pq.top().first;
        pq.pop();
        if(d!=dist[v])continue;
        for(auto i:g[v]){
            int u=es[i].other(v);
            int nd=es[i].c+d;
            if(dist[u]==-1||nd<dist[u]){
                pre[u]=i;
                dist[u]=nd;
                pq.emplace(nd,u);
            }
        }
    }
    rep(i,m)mark[i]=false;
    path={};

    if(dist[n-1]==-1)return;

    int v=n-1;
    int tmp=0;
    while(v!=0){
        int i=pre[v];
        tmp+=es[i].c;
        path.pb(i);
        mark[i]=true;
        v=es[i].other(v);
    }
    reverse(all(path));
}

int d0[nmax][nmax],d1[nmax][nmax];

void slv(int*res,int&best){
    build();
    sssp();
    best=dist[n-1];

    rep(i,n)rep(j,n){
        int w=(i==j?0:-1);
        d0[i][j]=d1[i][j]=w;
    }
    rep(i,m){
        E&e=es[i];
        upd(d0[e.u][e.v],e.c);
        if(!mark[i])
            upd(d1[e.u][e.v],e.c);
    }
    rep(k,n)rep(i,n)rep(j,n){
        upd(d0[i][j],comb(d0[i][k],d0[k][j]));
        upd(d1[i][j],comb(d1[i][k],d1[k][j]));
    }

    //not on path
    rep(i,m)if(!mark[i]){
        E&e=es[i];
        res[i]=best;
        upd(res[i],comb(d0[0][e.v],e.c,d0[e.u][n-1]));
    }

    if(dist[n-1]==-1)return;

    rep(i,m)if(mark[i])res[i]=-1;

    //on path
    rep(i,m)if(mark[i])res[i]=-1;
    rep(i,si(path))rng(j,i,si(path)){
        int a=es[path[i]].u;
        int b=es[path[j]].v;
        int w=comb(d0[0][a],d1[a][b],d0[b][n-1]);
        rng(k,i,j+1)
            upd(res[path[k]],w);
    }
}

int res[2][mmax];

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin>>n>>m;
    rep(i,m)cin>>es[i];

    int ans=-1;

    int w0,w1;

    slv(res[0],w0);
    rep(i,m){
        E&e=es[i];
        if(e.u==0)e.u=n-1;
        else if(e.u==n-1)e.u=0;
        if(e.v==0)e.v=n-1;
        else if(e.v==n-1)e.v=0;
    }
    slv(res[1],w1);
    rep(i,m){
        upd(ans,comb(res[0][i],res[1][i],es[i].d));
    }
    upd(ans,comb(w0,w1));

    cout<<ans<<'\n';
}

猜你喜欢

转载自www.cnblogs.com/iefnah06/p/12300391.html
今日推荐