【洛谷P5590】赛车游戏

差分约束

题目大意:

给定一张简单有向图。要给每条边添加 $[1,9]$ 中的整数权值,使得所有 $1$ 到 $n$ 的路径的长度都相等。

题解:

考虑 $d_i$ 表示从 $1$ 到 $i$ 的路径长度。那么我们只需得到一组合法的 $d_i$,然后有边相连的两点 $u\rightarrow v$ 之间的边权就为 $d_v-d_u$。

由限制条件可得,存在 $u\rightarrow v$ 的边时,$1\leq d_v-d_u\leq 9$。

拆开来,$d_v-d_u\geq 1$ 和 $d_v-d_u\leq 9$。

转化一下得 $d_u\leq d_v-1$ 和 $d_v\leq d_u+9$。

容易想到差分约束模型,跑 SPFA 求出一组解即可。

如果出现负环或两点不连通则为无解。

如果负环出现在无法到达 $n$ 的路径上,则并不会有影响,这些边随便标即可。

时间复杂度 $O(nm)$。

Code:

#include<iostream>
#include<vector>
#include<queue>
const int N=2333,inf=1e9;
using namespace std;
vector<int>G[N];
int n,m,head[N],cnt,dis[N],iq[N];
pair<int,int>E[N];
bool ok[N],vis[N];
struct edge{
    int to,nxt,dis;
}e[N<<1];
int get(int now){
    if(ok[now])return 1;
    vis[now]=1;
    for(int to:G[now])
        if(!vis[to])
            ok[now]|=get(to);
    vis[now]=0;
    return ok[now];
}
inline void addedge(int u,int v,int w){
    e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
}
queue<int>q;
bool spfa(){
    for(int i=2;i<=n;++i)dis[i]=inf;
    for(q.push(1);!q.empty();){
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt)
            if(dis[e[i].to]>dis[u]+e[i].dis){
                dis[e[i].to]=dis[u]+e[i].dis;
                if(!vis[e[i].to]){
                    if(++iq[e[i].to]>=n)return 0;
                    vis[e[i].to]=1;
                    q.push(e[i].to);
                }
            }
    }
    return 1;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int u,v;
        cin>>u>>v;
        G[u].push_back(v);
        E[i]=make_pair(u,v);
    }
    ok[n]=1;
    ok[1]=get(1);
    if(!ok[1]){
        cout<<"-1\n";
        return 0;
    }
    for(int f=1;f<=n;++f)
        for(int to:G[f])
            if(ok[f]&&ok[to])
                addedge(f,to,9),addedge(to,f,-1);
    if(!spfa()){
        cout<<"-1\n";
        return 0;
    }
    cout<<n<<' '<<m<<'\n';
    for(int i=1;i<=m;++i){
        int u=E[i].first,v=E[i].second;
        if(dis[v]==inf||dis[u]==inf)cout<<u<<' '<<v<<" 6\n";
        else
            cout<<u<<' '<<v<<' '<<dis[v]-dis[u]<<'\n';
    }
    return 0;
}