【Codeforces Round #669 (Div. 2) E】Egor in the Republic of Dagestan

题目链接

点我呀

翻译

你是小 \(A\) 的管家,小 \(A\) 要从点 \(1\) 到点 \(n\),点与点之间的边(有向边)有黑色(0)和白色(1)两种, 你可以给每个点涂色 (黑色/白色)。

黑色的点,只能沿着黑色的边接着走,白色的点同理,即如果 \(x\) 是黑色的,那么你接下来只能沿着边 \((x,y)\) 走,这条边 \((x,y)\) 也得是黑色的。

每个点的颜色由你决定,你不想让小 \(A\) 太快到达点 \(n\) (到达点 \(n\) 的最短路是所有涂色方案中最长的),或者最好就不让他到达点 \(A\) (畸形的爱)。

让你输出涂色方案,以及该方案下到达点 \(n\) 的最短路。

题解

就直接说做法了。

首先将所有的边方向,转换成从点 \(n\) 到点 \(1\) 的问题。

这样处理,就能保证到达点 \(x\) 的话,点 \(x\) 是什么颜色的,则走到 \(x\) 这个点经过的边也对应的是什么颜色。

每个点有两种染色的可能, 黑/白。

那么我们就设 \(b[x]\) 表示到达点 \(x\)\(x\) 染成了黑色(\(black\)) 的最短路(这里的最短路就是最坏情况下的最短路, 所以等下你会发现这个 \(b[x]\) 的值不是由某个
\(b[prex]\) 转移来的,而是经过另外一个 \(dp[prex]\) 转移过来的), \(w[x]\) 表示到达点 \(x\)\(x\) 染成了白色的最短路。

此外还要设一个 \(dp[x]\) 表示到达点 \(x\) 最坏情况需要的最短路, \(b[x]\)\(w[x]\) 的值就是由这个 \(dp\) 的值转移得到的。

最后,再定义一个数组 \(col[N]\) 来记录每个节点最后的颜色。

这个 \(col\) 数组显然可以根据 \(b[x]\)\(c[x]\) 的大小得到,哪一个大,就染成对应的颜色即可 (一样大则任意涂, 因为到了这个点以后, 后面的路径就和当前点的颜色无关了, 这也是图反向之后的好处)。

\(b\) 数组和 \(w\) 数组的生成过程和求最短路问题类似。

最开始的时候,令 b[n]=w[n]=dp[n]=0, 其他的 \(b,w,dp\) 的值都设为无穷大。

对于所有染色完毕的点 (这里如果 某个点 \(x\)\(max(b[x],w[x])\) 的值是无穷大, 即 \(b[x]\)\(w[x]\) 的值中
有一个还没算出来, 就认为该点还没染色完毕, 否则像初始的点 \(n\), \(b\)\(w\) 的最大值都是 \(0\), 则认为它染色完毕了, 也即这个点染成黑色
或白色的最短路都算出来了), 选择其中 \(dp\) 值最小的点 \(x\), 对于所有的出边 \((x,y)\), 如果边是黑边, 那么 b[y] = min(b[y],dp[x]+1)。如果是白边,则执行 w[y] = min(w[y],dp[x]+1)

\(b[y]\)\(w[y]\) 的值都算出来以后,令 dp[y] = max(b[y],w[y]) ,也即 \(y\) 有两种染色方案,选择从 \(1\)\(y\) 的最短路最长的那一种染色方案。

(这个时候 \(y\) 的颜色也就确认了), 那么将 \(y\) 加入队列中, 供下一次取出进行节点的更新。

这里对 \(col\) 数组的赋值可以等到 \(bfs\) 做完之后再一遍 \(for\) 循环得到(根据 \(b[i]\)\(w[i]\) 的比较结果获得)。

最后输出 \(dp[1]\) 的值就可以了 (如果 \(dp[1]\) 的值为无穷大, 说明有染色方案可以让小 \(A\) 无法从 \(1\)\(n\)),那么直接输出 \(-1\) 即可。

感性理解一下,就是求最短路,然后某个点的染色方案,根据到这点为黑色和白色的最短路的大小判断,谁大就染成对应颜色。这样的染色方案就能使得到达每个点的最短路是最长的了。

代码

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

const int N = 5e5;

int n, m;
int b[N + 10], w[N + 10], dp[N + 10], col[N + 10];
vector<int> bg[N + 10], wg[N + 10];
queue<int> dl;

int main(){
    #ifdef LOCAL_DEFINE
        freopen("E://9.AlgorithmCompetition//Visitor.txt","r",stdin);
    #endif
    ios::sync_with_stdio(0),cin.tie(0);
    cin >> n >> m;
    for (int i = 1;i <= m; i++){
        int u, v, t;
        cin >> u >> v >> t;
        if (t == 0){
            bg[v].push_back(u);
        }else{
            wg[v].push_back(u);
        }
    }
    for (int i = 1;i <= n; i++){
        b[i] = w[i] = dp[i] = n;
    }
    b[n] = w[n] = dp[n] = 0;
    dl.push(n);
    while (!dl.empty()){
        int x = dl.front();dl.pop();
        int len = bg[x].size();
        for (int i = 0;i < len; i++){
            int y = bg[x][i];
            if (b[y] > dp[x] + 1){
                b[y] = dp[x] + 1;
                if (max(b[y],w[y])<n){
                    dp[y] = max(b[y],w[y]);
                    dl.push(y);
                }
            }
        }
        len = wg[x].size();
        for (int i = 0;i < len; i++){
            int y = wg[x][i];
            if (w[y] > dp[x] + 1){
                w[y] = dp[x] + 1;
                if (max(b[y],w[y])<n){
                    dp[y] = max(b[y],w[y]);
                    dl.push(y);
                }
            }
        }
    }
    if (dp[1] == n){
        cout << -1 << endl;
    }else{
        cout << dp[1] << endl;
    }
    for (int i = 1;i <= n; i++){
        if (b[i] < w[i]){
            cout << 1;
        }else{
            cout << 0;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值