【模板】缩点 tarjan+dp

题目背景

缩点+DP

题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

输出格式:

共一行,最大的点权之和。

输入输出样例

输入样例#1:
复制

2 2
1 1
1 2
2 1

输出样例#1: 复制

2

说明

n<=10^4,m<=10^5,点权<=1000

算法:Tarjan缩点+DAGdp

Tarjan+记忆化搜索;

缩点以后,重新建图,然后dp;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<bitset>
#include<ctime>
#include<deque>
#include<stack>
#include<functional>
#include<sstream>
//#include<cctype>
//#pragma GCC optimize(2)
using namespace std;
#define maxn 400005
#define inf 0x7fffffff
//#define INF 1e18
#define rdint(x) scanf("%d",&x)
#define rdllt(x) scanf("%lld",&x)
#define rdult(x) scanf("%lu",&x)
#define rdlf(x) scanf("%lf",&x)
#define rdstr(x) scanf("%s",x)
typedef long long  ll;
typedef unsigned long long ull;
typedef unsigned int U;
#define ms(x) memset((x),0,sizeof(x))
const long long int mod = 1e9 + 7;
#define Mod 1000000000
#define sq(x) (x)*(x)
#define eps 1e-3
typedef pair<int, int> pii;
#define pi acos(-1.0)
const int N = 1005;
#define REP(i,n) for(int i=0;i<(n);i++)
typedef pair<int, int> pii;
inline ll rd() {
	ll x = 0;
	char c = getchar();
	bool f = false;
	while (!isdigit(c)) {
		if (c == ‘-‘) f = true;
		c = getchar();
	}
	while (isdigit(c)) {
		x = (x << 1) + (x << 3) + (c ^ 48);
		c = getchar();
	}
	return f ? -x : x;
}

ll gcd(ll a, ll b) {
	return b == 0 ? a : gcd(b, a%b);
}
ll sqr(ll x) { return x * x; }

/*ll ans;
ll exgcd(ll a, ll b, ll &x, ll &y) {
	if (!b) {
		x = 1; y = 0; return a;
	}
	ans = exgcd(b, a%b, x, y);
	ll t = x; x = y; y = t - a / b * y;
	return ans;
}
*/

int n, m;
int idx;
int col[maxn], dp[maxn], sum[maxn];
int head[maxn];
int sk[maxn], top;
int dfn[maxn], low[maxn];
int tot;
int vis[maxn];
int val[maxn];

struct node {
	int u, v, nxt;
}edge[maxn];

int cnt;
void addedge(int x, int y) {
	edge[++cnt].v = y; edge[cnt].nxt = head[x]; head[x] = cnt;
}

void tarjan(int x) {
	sk[++top] = x; vis[x] = 1;
	low[x] = dfn[x] = ++idx;
	for (int i = head[x]; i; i = edge[i].nxt) {
		int v = edge[i].v;
		if (!dfn[v]) {
			tarjan(v);
			low[x] = min(low[x], low[v]);
		}
		else if (vis[v]) {
			low[x] = min(low[x], dfn[v]);
		}
	}
	if (dfn[x] == low[x]) {
		tot++;
		while (sk[top + 1] != x) {
			col[sk[top]] = tot; sum[tot] += val[sk[top]]; vis[sk[top--]] = 0;
		}
	}
}

void DP(int x) {
	int maxx = 0;
	if (dp[x])return;
	dp[x] = sum[x];
	for (int i = head[x]; i; i = edge[i].nxt) {
		int v = edge[i].v;
		if (!dp[v])DP(v);
		maxx = max(maxx, dp[v]);
	}
	dp[x] += maxx;
}
int x[maxn], y[maxn];

int main()
{
	//ios::sync_with_stdio(0);
	rdint(n); rdint(m);
	for (int i = 1; i <= n; i++)rdint(val[i]);
	for (int i = 1; i <= m; i++) {
		rdint(x[i]); rdint(y[i]); addedge(x[i], y[i]);
	}
	for (int i = 1; i <= n; i++)if (!dfn[i])tarjan(i);
	ms(edge); cnt = 0; ms(head);
	for (int i = 1; i <= m; i++) {
		if (col[x[i]] != col[y[i]]) {
			addedge(col[x[i]], col[y[i]]);
		}
	}
	int ans = 0;
	for (int i = 1; i <= tot; i++) {
		if (!dp[i]) {
			DP(i); ans = max(ans, dp[i]);
		}
	}
	cout << ans << endl;
	return 0;
}

原文地址:https://www.cnblogs.com/zxyqzy/p/10165401.html

时间: 2024-10-14 21:46:08

【模板】缩点 tarjan+dp的相关文章

UVA 11324.The Largest Clique tarjan缩点+拓扑dp

题目链接:https://vjudge.net/problem/UVA-11324 题意:求一个有向图中结点数最大的结点集,使得该结点集中任意两个结点u和v满足:要目u可以到达v,要么v可以到达u(相互可达也可以). 思路:同一个强联通分量中满足结点集中任意两个结点u和v满足:要目u可以到达v,要么v可以到达u(相互可达也可以).把强联通分量收缩点后得到scc图,让每个scc结点的权值等于他的结点数,则求scc图上权最大的路径.拓扑dp,也可以直接bfs,但是要建立一个新的起点,连接所有入度为0

BZOJ 1093: [ZJOI2007]最大半连通子图( tarjan + dp )

WA了好多次... 先tarjan缩点, 然后题意就是求DAG上的一条最长链. dp(u) = max{dp(v)} + totu, edge(u,v)存在. totu是scc(u)的结点数. 其实就是记忆化搜一下...重边就用set判一下 ------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring

【luogu3387】 【模板】缩点 [tarjan 缩点]

P3387 [模板]缩点 静下心来去看 其实真的很好理解 突然搞不懂我之前为什么死活都看不懂 参悟了学长的代码还有BYVoid的讲解 放一下BYVoid大佬的tarjan伪代码 帮助理解 还有各种变量的含义 (from黄学长 栈里的元素表示的是当前已经访问过但是没有被归类到任一强连通分量的结点dfn[u] 表示结点 u 在 DFS 中第一次搜索到的次序,通常被叫做时间戳 ow[u] 它表示从 u 或者以 u 为根的子树中的结点,再通过一条反祖边或者横叉边可以到达的时间戳最小的结点 v 的时间戳,

BZOJ 1512 [POI2006]Pro-Professor Szu Tarjan缩点+拓扑DP

题意: n个别墅以及一个主建筑楼,从每个别墅都有很多种不同方式走到主建筑楼,其中不同的定义是(每条边可以走多次,如果走边的顺序有一条不同即称两方式不同). 询问最多的不同方式是多少,以及有多少个别墅有这么多方式,按照顺序输出别墅编号. 如果最多不同方式超过了36500那么都视作zawsze 解析: 容易想到把边反向,问题转化成求从主建筑楼走向各个点的方案数. 对于一个强连通分量,显然我们可以看做是一个点,所以首先把图缩点. 缩点之后 我们设f[i]表示走到第i个点的方案数. 显然f[i]=∑f[

[SDOI2010] 所驼门王的宝藏 [建图+tarjan缩点+DAG dp]

题面传送门: 传送门 思路: 看完题建模,容易得出是求单向图最长路径的问题 那么把这张图缩强联通分量,再在DAG上面DP即可 然而 这道题的建图实际上才是真正的考点 如果对于每一个点都直接连边到它所有的后继节点,那么可以被卡掉(1e5个点在同一行上) 考虑改变思路,运用网络流建图中的一个常用技巧:把横边和竖边映射成点,再从每个点向所在横坐标.纵坐标代表的点连边即可 这样会有2e6+1e5个点,但是tarjan算法效率O(n),完全无压力 自由(和谐)门的话,目前还没有比较好的方法解决 上网看了一

[Bzoj 2427] [HAOI2010] 软件安装 tarjan缩点+树形DP

题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一 些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大). 但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j).幸运的 是,一个软件最多依赖另外一个软件.如果一个软件不能正常工作,那么它能够发挥的作用为0. 我们现在知道了软件之间的依赖关系:软件i依赖软件Di.现在请你设计出

【BZOJ1179】【Apio2009】Atm 强连通分量缩点+拓扑DP/拓扑最长路 kosaraju+tarjan+dfs转非递归三种代码

题解: 首先第一个阶段, 可以写kosaraju.也可以写tarjan. 这两种还都分递归和dfs转非递归. ----------------------------------四种方案. 第二个阶段,可以写拓扑DP 也可以写最长路 ----------------------------------乘上之前的,,八种方案. 本文写了kosaraju递归版,tarjan递归版,kosaraju非递归版. --只怪学校oj系统栈太小..都是逼得啊. 代码1(tarjan): #include <c

HDU 4005 The war Tarjan+dp

The war Problem Description In the war, the intelligence about the enemy is very important. Now, our troop has mastered the situation of the enemy's war zones, and known that these war zones can communicate to each other directly or indirectly throug

hdoj 2242 考研路茫茫——空调教室 【无向图求边双联通 缩点 + 树形dp】

考研路茫茫--空调教室 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2447    Accepted Submission(s): 721 Problem Description 众所周知,HDU的考研教室是没有空调的,于是就苦了不少不去图书馆的考研仔们.Lele也是其中一个.而某教室旁边又摆着两个未装上的空调,更是引起人们无限YY