对于图中的两个点 u u u 和 v v v,若分别存在一条路径使得 u → v , v → u u\to v,v\to u u→v,v→u,则称 ( u , v ) (u,v) (u,v) 强联通。
若对于一张图 G G G 中任意两个点都强联通,则称 G G G 为一个强连通图。
一张图往往由多个强联通子图组成(各个强联通图之间可能会有包含关系),对于那些最大的强联通子图(不存在包含关系),我们称其为强联通分量。
不严谨的说,强联通分量就是环。
若一个图是 D A G DAG DAG (有向无环图),我们可以根据其拓扑序的无后效性,在图上跑各种 d p , d f s dp,dfs dp,dfs,从而找到解。但如果一个图有环,那么后效性就会使我们非常尴尬(环会导致起初遍历过的点的答案发生改变)。
但是往往会有一些非常明显的贪心思路,例如一个环的贡献其实就是等于环上所有点的贡献之和。
因此,我们就可以把一个环 缩 成一个超级点。对图中的所有环进行类似操作,最终图就会转变成一个 D A G DAG DAG,且保留了环的有效贡献,此时我们就可以在图中放心跑各种无后效性的算法。
显然,横向边不构成一个包含 u u u 的环,后向边反之。因此,找强联通分量即为找到最外层的关于 u u u 的后向边。
我们可以用 d f s dfs dfs 中的栈来实现上述过程,因为 d f s dfs dfs 遍历的是一条由可以到达 u u u 的点和 u u u 可以到达的点组成的路径,我们可以借此来快速判断某一点是否能到达 u u u。
stack<int> p;
void tarjan(int u)
{
dfn[u]=low[u]=++tt;
p.push(u);
vis[u]=1; //在栈中
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(!dfn[v]) //还未遍历过
{
tarjan(v);
low[u]=min(low[u],low[v]); //尝试用 v 更新 u
}
else if(vis[v]) low[u]=min(low[v],low[u]); //v可以到达u,在同一个强联通中,因此选最早被遍历的
}
if(dfn[u]==low[u]) //u点为所在强联通分量中最早被遍历到的点
{
scc++;
while(1)//栈中,u点之后的所有点都是与u在同一个强联通分量中,与u不同的都已经被处理过了弹出了
{
int now=p.top();
//记录当前强联通分量的贡献
belong[now]=scc;
vis[now]=0;
p.pop();
if(now==u) break;
}
}
return ;
}
参考文献:
初探 tarjan 算法
TarJan,你真的了解吗
强联通分量算法解析
tarjan 详解
s o l u t i o n solution solution:显然,如果是一个环,那么肯定,把这个环中所有点都遍历一遍是这个环的最优贡献,因此我们按照此贪心利用 tarjan 缩环为点,得到一张 D A G DAG DAG,然后拓扑后跑 d p dp dp 即可。
s o l u t i o n solution solution:
首先,两头明星奶牛 u , v u,v u,v 肯定在同一强联通分量中。
证明:根据明星奶牛的定义, ∀ z ∈ G , z → u \forall z\in G ,z\to u ∀z∈G,z→u 且 ∀ z ∈ G , z → v \forall z\in G ,z\to v ∀z∈G,z→v,因此 u → v u\to v u→v 且 v → u v\to u v→u,所以 u , v u,v u,v 在同一个强联通分量中。
第二,明星奶牛所在的强联通分量是不会有出边的。
证明:考虑明星奶牛所在的强联通分量为 u u u,有出边为 u → v u\to v u→v。由于所有点都可以到达 u u u,因此所有点都可以经过 u u u 到达 v v v,也就是强联通分量 v v v 也是由明星奶牛构成的,但这与我们上一条总结的结论矛盾,因此明星奶牛所在的强联通分量是不会有出边的。
有了这两条结论,做法明了:缩点后找出边为零的点,若改点唯一,则答案为该强联通分量的大小,否则不存在明星奶牛。
显然环中的点任意可达,因此这个环买入的最小值为所有点的 min \min min ,出售的最大值为所有点的 max \max max,以此来缩点,更新强联通分量的权值,在 D A G DAG DAG 上拓扑跑 d p dp dp 即可。
设 f m i n i fmin_i fmini 为到达 i i i 点时的最小买入价格,则有 f m i n i = min ( f m i n j , a i ) fmin_i=\min(fmin_j,a_i) fmini=min(fminj,ai),其中 j j j 为可以到达 i i i 的点, a i a_i ai 为 i i i 点(实际上是一个强联通分量)的最小买入值。
设 f i f_i fi 为到达 i i i 点的最大利润,则有 f i = max ( f j , b i − f m i n i ) f_i=\max(f_j,b_i-fmin_i) fi=max(fj,bi−fmini),其中 j j j 为可以到达 i i i 的点, b i b_i bi 为 i i i 点(实际上是一个强联通分量)的最大出售值。
答案即为 f n 点 所 在 的 强 联 通 分 量 编 号 f_{n点所在的强联通分量编号} fn点所在的强联通分量编号