【模板】缩点
题目描述
给定一个 n n n 个点 m m m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式
第一行两个正整数 n , m n,m n,m
第二行 n n n 个整数,其中第 i i i 个数 a i a_i ai 表示点 i i i 的点权。
第三至 m + 2 m+2 m+2 行,每行两个整数 u , v u,v u,v,表示一条 u → v u\rightarrow v u→v 的有向边。
输出格式
共一行,最大的点权之和。
样例 #1
样例输入 #1
2 2
1 1
1 2
2 1
样例输出 #1
2
提示
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 4 1\le n \le 10^4 1≤n≤104, 1 ≤ m ≤ 1 0 5 1\le m \le 10^5 1≤m≤105, 0 ≤ a i ≤ 1 0 3 0\le a_i\le 10^3 0≤ai≤103。
题解:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 1, N = 1e4 + 1;
int n, m, val[N], cnt = 0, head[N] = { 0 }, vis[N] = { 0 }, low[N], dfn[N], times = 0, st[N], top = -1;
int belong[N], rep[N] = { 0 }, num = 0; long long tans = 0, ans = 0;
struct Edge {
int from, to, next;
}e[maxn];
void add(int u, int v) {
cnt++;
e[cnt].from = u;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
return f * x;
}
void tarjan(int k) {
vis[k] = 1;
low[k] = dfn[k] = ++times;
st[++top] = k;
int r = top;
for (int i = head[k]; i; i = e[i].next) {
int v = e[i].to;
if (!dfn[v]) {
tarjan(v);
low[k] = min(low[k], low[v]);
}
//只有v被访问过且v还不属于任何强连通分量的情况下才更新low[u]
else if (vis[v]) {
low[k] = min(low[k], dfn[v]);
}
}
//low[k]==dfn[k]表明找到了一个强连通分量
//单个点也算一个强连通分量
if (low[k] == dfn[k]) {
//num表示强连通分量的个数
num++;
//将同属于一个强连通分量的一坨点出栈
for (int i = r; i <= top; i++) {
//vis标记为0表明这个点已经找到了属于它的强连通分量
vis[st[i]] = 0;
//belong[y]=x;表示y这个点属于编号为x的强连通分量
belong[st[i]] = num;
//rep数组存放单个强连通分量的点权之和
rep[num] += val[st[i]];
}
top = r - 1;
}
}
//重新建图
void rebuild() {
cnt = 0;
memset(head, 0, sizeof(head));
for (int i = 1; i <= m; i++) {
int u = e[i].from, v = e[i].to;
if (belong[u] != belong[v]) {
add(belong[u], belong[v]);
}
}
}
//进行一遍完整的dfs就可找到答案
void dfs(int k,int sum) {
sum += rep[k];
tans = sum > tans ? sum : tans;
for (int i = head[k]; i; i = e[i].next) {
int t = e[i].to;
dfs(t, sum);
}
}
int main() {
n = read(); m = read();
for (int i = 1; i <= n; i++)//读入各点权
val[i] = read();
for (int i = 1; i <= m; i++) {
int u, v; u = read(), v = read();
add(u, v);
}
for (int i = 1; i <= n; i++) {
if (dfn[i] == 0)
tarjan(i);
}
rebuild();
memset(vis, 0, sizeof(vis));
//图不一定连通
for (int i = 1; i <= num; i++) {
tans = 0;
dfs(i, 0);
ans = tans > ans ? tans : ans;
}
cout << ans;
}