缩点其实算Tarjan求强连通分量的一个应用吧.
由于一个强连通分量内部可以互通.所以可以将其缩成一个点.把图变成DAG.然后就可以跑一跑dp啥的了.
模板题目1:洛谷P3387
题目大意:
给你一个有向连通图.每个点有点权.问你找出一条路径使得点权值最大.每个点和每个边能够经过多次.但是只算一次点权.
题目思路:
缩点,然后DAG上跑最长路即可.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 4;
vector<int> e[maxn];
int dfn[maxn] , low[maxn] , vis[maxn] , a[maxn] , val[maxn];
stack<int> s;
int cnt = 0 , tot = 0; // tot 用来给强连通分量标号,用来缩点建边
int id[maxn];
vector<int> new_e[maxn];
void tarjan (int u)
{
dfn[u] = low[u] = ++cnt;
vis[u] = 1;
s.push(u);
for (auto v : e[u]){
if (!dfn[v]) {
tarjan(v);
low[u] = min (low[u] , low[v]);
}
else if (vis[v]){
low[u] = min (low[u] , dfn[v]);
}
}
if (dfn[u] == low[u]){
++tot;
while (1){
int now = s.top();
s.pop();
val[tot] += a[now];
id[now] = tot;
vis[now] = 0;
if (now == u) break;
}
}
}
int dp[maxn];
int dfs (int u)
{
if (~dp[u]) return dp[u];
int ans = 0;
for (auto v : new_e[u]){
ans = max (ans , dfs(v));
}
return dp[u] = ans + val[u];
}
int main()
{
ios::sync_with_stdio(false);
int n , m;cin >> n >> m;
for (int i = 1 ; i <= n ; i++){
cin >> a[i];
}
for (int i = 1 ; i <= m ; i++){
int x , y; cin >> x >> y;
e[x].push_back(y);
}
for (int i = 1 ; i <= n ; i++){
if (!dfn[i]) tarjan(i);
}
for (int i = 1 ; i <= n ; i++){
for (auto v : e[i]){
if (id[i] != id[v]){
new_e[id[i]].push_back(id[v]);
}
}
}
// cout << tot << endl;
memset(dp , -1 , sizeof dp);
int ans = 0;
for (int i = 1 ; i <= tot ; i++){
dfs(i);
ans = max (ans , dp[i]);
}
cout << ans << endl;
return 0;
}
模板题目2:洛谷P2341-受欢迎的牛
题目大意:
给你一张n点m边的有向连通图.问你有多少个点是其他所有点能够达到的.
n , m ≤ 1 e 5 n,m \leq 1e5 n,m≤1e5
题目思路:
缩点的一个应用。稍微需要一些推理。
考虑同一个强连通分量之间可以互通。那么对于有若干个强连通分量的图:当且仅当图中只有一个出度为0的强连通分量时,存在答案。答案即为这个分量的大小
解释:假设一个强连通分量S有出度。设到达的分量为T。那么T是无法到达S的。
因为如果T能够达到S的话,T,S就能够互达了。T,S就是同一个连通分量了。与假设冲突了. 所以只有出度为0的强连通分量是其他所有点能够互通的。
而如果有多个出度为0的分量。则它们之间是不能互通的。也不存在答案。
分析到这里,直接套tarjan算法缩点求出度即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
vector<int> e[maxn];
int dfn[maxn] , low[maxn] , du[maxn] , vis[maxn] , id[maxn] , sz[maxn] , cnt , tot;
stack<int> s;
void tarjan (int u)
{
dfn[u] = low[u] = ++cnt;
s.push(u);
vis[u] = 1;
for (auto v : e[u]){
if (!dfn[v]){
tarjan(v);
low[u] = min (low[u] , low[v]);
}
else if (vis[v]){
low[u] = min (low[u] , dfn[v]);
}
}
if (dfn[u] == low[u]){
++tot;
while (1){
int now = s.top();
s.pop();
vis[now] = 0;
id[now] = tot;
sz[tot]++;
if (now == u) break;
}
}
}
int main()
{
ios::sync_with_stdio(false);
int n , m; cin >> n >> m;
for (int i = 1 ; i <= m ; i++){
int x , y;cin >> x >> y;
e[x].push_back(y);
}
for (int i = 1 ; i <= n ; i++){
if (!dfn[i]) tarjan(i);
}
for (int i = 1 ; i <= n ; i++){
for (auto v : e[i]){
if (id[i] != id[v]){
du[id[i]]++;
}
}
}
int cnt = 0 , res = 0;
for (int i = 1 ; i <= tot ; i++){
if (du[i] == 0) {
cnt++;
res = sz[i];
}
}
if (cnt >= 2) cout << "0" << endl;
else cout << res << endl;
return 0;
}