https://codeforces.com/contest/1407/problem/E
题意:给一个有向图,每条边有一个类型(0或者1),什么类型的城市就只能走什么类型的边,你需要给每个城市指定类型(0/1),使得从1到n 的最短路最长,甚至长到1无法到达n
思路:开始想的时候感觉是个dp,而且要反向建边。这道题补了挺久的。
首先要明确这是个dp,那么dp的话正常开始是正向入手,但是正向很难处理,为什么呢?
比如 1---(0,1)---->2----(1,0)---->3从1转移到2节点的时候,2节点取0/1取决后面的2后面的边权,没法很好确定2这个点最后该取哪色
|------(1,0)------->4
但是反向建边就可以保证用边权构造点的颜色。比如 2<----(0)-----4,那么可以确定2是0。或者2<------(1,0)----4,可以保证2可以取1/0;
而且这个题你会发现,只有当一个点同时被1和0都更新过了,才能保证这个图必然是能最终连通的。
比如我2这个节点反向边上只有0,那么我正图中操作的时候令其为1,那么最后就能让答案是-1。
那么考虑dp。
b[x]:x点为黑色,到x点的最短路。
w[x]:x点为白色时,到x点的最短路。
这两个从刚才的推导中可以得到。因为一个点要被w[x]和b[x]都更新过才能使得最后能到终点(反图终点为1号节点)
而且此时边权为01,直接bfs去做。
因为是bfs,一个点先遍历到肯定比后来其他点遍历到这个点更优,所以一个点的w[x]被更新过了,对应的这个点的w[x]就不更新了,b[x]亦然。
而且因为一个点会被0和1更新两回,所以不能用vis[]来判在0和1的b[]/w[]中是否被更新过,因为这样会使得更新过就不更新了。
那么只用这两个转移可以嘛?
比如我用b[x]=min(b[x],b[y]+1/w[y]+1);
这个b[x]开始初始化为0x3f3f3f3f.b[x]成为b[y]+1和w[y]+1中小的那个。
x--->z的时候用b[x]+1和w[x]+1更小的那个更新b[z],w[z];
但是实际上不对。题目使得最短路更大,这里节点x的选择应该选择b[y]和w[y]中更大的那个来更新,这样保证更新出来的最短路是更大的。所以还需要一个dp[x]。
设置dp[x]:x为任意色,到达x点时的最短路;
转移为:
b[y]=min(b[y],dp[x]+1);
w[y]=min(w[y],dp[x]+1);
dp[y]=max(w[y],b[y]);
dp[1]最后是否为无穷来判能否到1点。如果w[x]>=b[x]--->输出1,不然就0。
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=5e5+100;
typedef long long LL;
LL w[maxn],b[maxn],dp[maxn],n,m;
struct P{
LL to,col;
};
vector<P>g[maxn];
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
cin>>n>>m;
for(LL i=1;i<=m;i++){
LL x,y,c;cin>>x>>y>>c;
g[y].push_back({x,c});
}
memset(w,0x3f,sizeof(w));
memset(b,0x3f,sizeof(b));
memset(dp,0x3f,sizeof(dp));
w[n]=b[n]=dp[n]=0;
queue<LL>q;
q.push(n);
while(!q.empty())
{
LL t=q.front();q.pop();
for(LL i=0;i<g[t].size();i++){
LL v=g[t][i].to;
LL c=g[t][i].col;
if(c==0)
{
if(b[v]<=n) continue;
b[v]=min(b[v],dp[t]+1);
if(max(b[v],w[v])<=n){
dp[v]=max(b[v],w[v]);
q.push(v);
}
}
else{
if(w[v]<=n) continue;
w[v]=min(w[v],dp[t]+1);
if(max(b[v],w[v])<=n){
dp[v]=max(b[v],w[v]);
q.push(v);
}
}
}
}
if(dp[1]>n)
{
cout<<"-1"<<endl;
for(LL i=1;i<=n;i++){
if(w[i]>=b[i]) cout<<1;
else cout<<0;
}
}
else{
cout<<dp[1]<<endl;
for(LL i=1;i<=n;i++)
{
if(w[i]>=b[i]) cout<<1;
else cout<<0;
}
cout<<endl;
}
return 0;
}