例题一: 有向图缩点 link
思路:
大家一开始可能都会想到直接用SPFA跑最长路,时间复杂度为 O ( n m ) O(nm) O(nm),
看看数据,TLE是必然的。
那有没有时间更优的方法——强连通分量。
分析题意,每个点点权只被计算一次,允许一条边走多次,
那我们考虑用Tarjan来进行缩点,
使图变成有向无环图,再进行 D P DP DP。
设转移方程为: f = m a x ( f u + d i s v , f v ) f=max(f_u+dis_v,f_v) f=max(fu+disv,fv)
f i f_i fi表示以i作为终点的路径所经过的最大点权和。
#include<queue>
#include<cstdio>
#include<stack>
using namespace std;
const int N = 1e5 + 10;
struct node {
int to, nxt;} e[N], e_[N];
int n, m, a[N], head[N], KK, tot;
int x, y, dfn[N], low[N], num, in[N], ans;
int sum[N], head_[N], KK_, du[N], dis[N];
queue <int> q;
stack <int> s;
void add(int x, int y) {
e[++KK] = (node){
y, head[x]}; head[x] = KK;}
void add_(int x, int y) {
e_[++KK_] = (node){
y, head_[x]}; head_[x] = KK_;}
void Tarjan(int now)
{
dfn[now] = low[now] = ++num;
s.push(now);
for (int i = head[now]; i; i = e[i].nxt)
if (!dfn[e[i].to]) Tarjan(e[i].to), low[now] = min(low[now], low[e[i].to]);
else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]);
if (dfn[now] == low[now])
{
in[now] = ++tot;
sum[tot] = a[now];
int tmp = s.top();
while (tmp != now) s.pop(),in[tmp] = tot,sum[tot] += a[tmp], tmp = s.top();
s.pop();
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= m; i++) scanf("%d %d", &x, &y), add(x, y);
for (int i = 1; i <= n; i++)
if (!dfn[i]) Tarjan(i);