1、[USACO08DEC] Trick or Treat on the Farm G - 洛谷
思路:求奶牛第一次走到走过的点时,所经过的路径,那么走过的路径会出现环,奶牛总共走过的距离即为到环的距离和环上的距离。
算法实现:先用tarjan求出强连通分量,scc缩点得到有向无环图,缩出的点的权值即为环内的总路径,依次建立新图,在新图的基础上进行记忆化搜索,最终得出答案。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <stack>
#include <bitset>
#include <set>
#include <map>
#define int long long
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
#define ss second
#define ff first
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 10;
vector<int> e[N];
vector<int> ne[N];
int nw[N];
int dfn[N], low[N], tot;
int stk[N], instk[N], top;
int scc[N], siz[N], cnt;
int ans[N];
int vis[N];
void tarjan(int x)
{
dfn[x] = low[x] = ++tot;
stk[++top] = x, instk[x] = 1;
for (int y : e[x])
{ // 入
if (!dfn[y])
{
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (instk[y])
{
low[x] = min(low[x], dfn[y]);
}
}
if (dfn[x] == low[x])
{ // 离
int y;
++cnt;
do
{
y = stk[top--];
instk[y] = 0;
scc[y] = cnt;
++siz[cnt];
} while (y != x);
}
}
int dfs(int x)
{
ans[x] = siz[x]; // 最初的答案为环的大小
for (int y : ne[x]) // 因为有向图连自题目要求,所以一旦有路径可以走通,那么
// 奶牛就必须行动,因为要走到对应的需求的隔间
{
if (!ans[y]) // 如果能走通的地方不是环,则继续向下走
ans[x] += dfs(y);
else
{
ans[x] += ans[y]; // 如果走通的地方是环,那么就加上环的大小而退出
break;
}
}
return 0;
}
signed main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
int j;
cin >> j;
e[i].push_back(j);
if (i == j)
vis[i] = 1;
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
tarjan(i);
}
for (int x = 1; x <= n; x++) // 建立新图
{
for (int y : e[x])
{
int a = scc[x];
int b = scc[y];
if (a != b) // 如果两个强连通分量没有相连,在新图中连出来
ne[a].push_back(b);
}
}
for (int i = 1; i <= cnt; i++)
{
if (siz[i] != 1)
{
ans[i] = siz[i];
}
}
for (int i = 1; i <= cnt; i++)
{
dfs(i); // 对于每一个分量都进行dfs
// 依据奶牛当前所处的连通块来决定最终奶牛需要走多少路径。
// 如果奶牛在环上,则路径为环大小,如果在链上,为环到链上点的距离加环大小。
}
for (int i = 1; i <= n; i++)
{
cout << ans[scc[i]] << endl;
}
return 0;
}
模板题,需要考虑的是tarjan处理完的是拓扑逆序的点,所以求最长路应该倒着遍历。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <stack>
#include <bitset>
#include <set>
#include <map>
#define int long long
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
#define ss second
#define ff first
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 10;
vector<int> e[N];
vector<int> ne[N];
int dfn[N], low[N], tot;
int stk[N], instk[N], top;
int scc[N], siz[N], cnt;
int f[N];
int w[N];
int nw[N];
void tarjan(int x)
{
dfn[x] = low[x] = ++tot;
stk[++top] = x, instk[x] = 1;
for (int y : e[x])
{ // 入
if (!dfn[y])
{
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (instk[y])
{
low[x] = min(low[x], dfn[y]);
}
}
if (dfn[x] == low[x])
{ // 离
int y;
++cnt;
do
{
y = stk[top--];
instk[y] = 0;
scc[y] = cnt;
++siz[cnt];
} while (y != x);
}
}
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
w[i] = x;
}
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
e[u].push_back(v);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
tarjan(i);
}
for (int x = 1; x <= n; x++)
{
nw[scc[x]] += w[x];
for (int y : e[x])
{
int a = scc[x];
int b = scc[y];
if (a != b)
{
ne[a].push_back(b);
}
}
}
for (int x = cnt; x; x--)
{
if (f[x] == 0)
{
f[x] = nw[x];
}
for (int y : ne[x])
{
f[y] = max(f[y], f[x] + nw[y]);
}
}
int ans = 0;
for (int i = 1; i <= cnt; i++)
{
ans = max(ans, f[i]);
}
cout << ans << endl;
return 0;
}