【ybtoj】【强连通分块】【Tarjan】【DP】【例题1】有向图缩点

【例题1】有向图缩点


Link

传送门
题目


解题思路

关于解释Tarjan这件事,这篇博客就是yyds

d i s i dis_i disi为以i为终点的最大值, s i s_i si为i点的点权值
设有一条边是 i − > j i->j i>j
d i s j = m a x ( d i s j , d i s i + s j ) dis_j = max(dis_j, dis_i+s_j) disj=max(disj,disi+sj)

优化:Tarjan -》取消了环的无用性
一个强连通分块的值是固定的,先用Tarjan求出来
再用缩出来点进行DP


Code

#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;

struct DT{
	int to, next;
}Ta[500000], a[500000];
int n, m, dye, ans, num, Tnum, now, top, x[10100], y[10100];
int Ts[10100], Thead[10100], low[10100], dfn[10100], hep[10100], co[10100];
int s[10100], head[10100], dis[10100], times[10100];
queue<int>q;

void Tarjan(int x) {
	low[x] = dfn[x] = ++now;
	hep[++top] = x;
	for (int i = Thead[x]; i; i = Ta[i].next) {
		if (!dfn[Ta[i].to]) {
			Tarjan(Ta[i].to);
			low[x] = min(low[x], low[Ta[i].to]);
		}
		else if (!co[Ta[i].to])
			low[x] = min(low[x], dfn[Ta[i].to]);
	}
	if (low[x] == dfn[x]) {
		++dye;
		while(hep[top + 1] != x)
			co[hep[top--]] = dye;
	}
}

void answer() {
	for (int i = 1; i <= dye; i++) {
		dis[i] = s[i];
		if (!times[i])  //用拓扑顺序DP
			q.push(i);
	}
	while(!q.empty()) {
		int x = q.front();
		q.pop();
		for (int i = head[x]; i; i = a[i].next) {
			dis[a[i].to] = max(dis[a[i].to], dis[x] + s[a[i].to]);  //DP
			times[a[i].to]--;  //拓扑
			if (!times[a[i].to])
				q.push(a[i].to);
		}
	} 
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d", &Ts[i]);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &x[i], &y[i]);
		Ta[++Tnum] = (DT){y[i], Thead[x[i]]};
		Thead[x[i]] = Tnum;
	}
	for (int i = 1; i <= n; i++)
		if (!dfn[i])
			Tarjan(i);
	for (int i = 1; i <= n; i++)
		s[co[i]] += Ts[i];  //将缩出的点的值更新
	for (int i = 1; i <= m; i++)
		if (co[x[i]] != co[y[i]]) {  //重新对点进行建图
			times[co[y[i]]]++;
			a[++num] = (DT){co[y[i]], head[co[x[i]]]};
			head[co[x[i]]] = num;
		}
	answer();
	for (int i = 1; i <= dye; i++)
		ans = max(ans, dis[i]);
	printf("%d", ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值