牧师约翰最忙碌的一天
解释一下具体方案的选择
我们来思考一下如果给了我们最终方案,我们应该如何选择。产生矛盾当且仅当放在同一水平线的对应的两个点同时选择。也就是说我们的方案对于每对放在同一水平线上对应的两个点应该是选择且之选择一个
那么来看蓝书怎么搞的
首先是第一种方法。如果我们按照拓扑正序去做,有可能会导致我们同时选择放在同一水平线的对应的两个点。设\(i,\text{opp}[i]\)为这两个对应的点且存在一条边\((i,\text{opp}[i])\),那么产生这个问题的原因就是\(i\)比\(\text{opp}[i]\)先入队,导致在确定了\(i\)要选之后,我们没办法确定\(\text{opp}[i]\)不选,反之我们必须要选\(\text{opp}[i]\),这就说明这种方案不合法;而拓扑逆序就很好地避免了这个问题,此时有\(\text{opp}[i]\)比\(i\)先入队,我们确定了\(\text{opp}[i]\)要选之后,当然可以确定\(i\)不选,此时\((i,\text{opp}[i])\)就不起作用了(也就是在拓扑逆序的过程中,对于任意一条横跨两个部的边,终点一定都比起点先入队,于是就可以确定终点选择而起点不选择,不会出现矛盾的情况);所以蓝书上\(\text{val}[k]=0\)的意思就是\(k\)这个SCC要选,反之不选;而由于我们最后循环的\(i\)的范围都是在\([1,N]\)之间,于是\(\text{val[c}[i]]=0\)说明\(i\)取值为\(0\)的域所在的SCC要选择,所以\(i\)的赋值为\(0\),反之说明\(i\)取值为\(0\)的域所在的SCC不选择,于是\(i\)取值为\(1\)的域所在的SCC要选择,\(i\)赋值为\(1\)
然后来看看第二种方法,其实也就是利用tarjan执行完之后,SCC的逆序是一个拓扑序,但我们需要证明原图中拓扑序的逆序列是反图的拓扑序(也就是原图的拓扑逆序)
其实稍微想一下就知道,我们倒着执行原图的拓扑序,假设准备执行当前节点时,我们来想一下此时序列的意义
原图的拓扑序中,当前节点的后面的节点是在当前节点之后删除的,也就是说当前节点在原图的出边的终点一定排在原图拓扑序列中当前节点的后面,所以在反图上,我们倒着执行到当前节点的时候,当前节点在反图上入边的起点(也就是原图上出边的终点)一定都执行完了,所以当前节点在反图上的度数一定是\(0\),也就没有问题
然后我们从小到大遍历\(c[i]\),执行第一种方法的思路,对于\(c[i]\)和\(\text{opp}[c[i]]\),如果\(c[i]\)先被遍历到就说明\(c[i]\)要选择而\(\text{opp}[c[i]]\)不选择,反之说明\(c[i]\)不选择而\(\text{opp}[c[i]]\)要选择,等价语句就是val[i]=c[i]>c[opp[i]]
(可以想一下为什么)