模板:tarjan缩点+重新建边+最短路

洛谷P3387 【模板】缩点

题目背景

缩点+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

直接上代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

int last[100100],len=0,sccno[10010]={0},scc=0,n,m,u,v,w[10010]={0};
int dfn[10010]={0},low[10010]={0},dfscnt=0,wscc[10010]={0};
int f[100010]={0},t[100010]={0},ans=-2147483648,dis[10010]={0};
bool vis[10010];

stack <int> s;

struct edge {
int next,to;
}e[100100];

int gi() {
char c=getchar();bool f=0;int a=0;
while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=1;c=getchar();}
while (c>=‘0‘&&c<=‘9‘) {a=a*10+c-‘0‘;c=getchar();}
return f?-a:a;
}

void add(int x,int y) {
e[++len].to=y;
e[len].next=last[x];
last[x]=len;
}

void init() {
memset(last,-1,sizeof(last));
n=gi();m=gi();
for (int i=1;i<=n;i++) w[i]=gi();
for (int i=1;i<=m;i++) {
u=gi();v=gi();add(u,v);f[i]=u;t[i]=v;
}
}

int tarjan(int r) {
s.push(r);
dfn[r]=low[r]=++dfscnt;
for (int i=last[r];i!=-1;i=e[i].next) {
int y=e[i].to;
if (!dfn[y]) {tarjan(y);low[r]=min(low[r],low[y]);}
else if (!sccno[y]) low[r]=min(low[r],dfn[y]);
}
if (dfn[r]==low[r]) {
scc++;
while (1) {
int x=s.top();sccno[x]=scc;wscc[scc]+=w[x];
s.pop();if (x==r) break;
}
}
}

void build() {
len=0;
memset(last,-1,sizeof(last));
for (int i=1;i<=m;i++) {
if (sccno[f[i]]!=sccno[t[i]]) add(sccno[f[i]],sccno[t[i]]);
}
}

int bfs(int x) {
memset(dis,-1,sizeof(dis));
memset(vis,false,sizeof(vis));
dis[x]=wscc[x];
queue <int> q;
vis[x]=true;q.push(x);
while (!q.empty()) {
int u=q.front();q.pop();vis[u]=false;
for (int i=last[u];i!=-1;i=e[i].next) {
int v=e[i].to;
if (dis[v]<dis[u]+wscc[v]) {
dis[v]=dis[u]+wscc[v];
if (!vis[v]) {vis[v]=true;q.push(v);}
}
}
}
for (int i=1;i<=scc;i++) ans=max(dis[i],ans);
}

int main() {
init();
for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
build();
for (int i=1;i<=scc;i++) bfs(i);
printf("%d\n",ans);
return 0;
}

飞一样的感觉,这一题我是一遍过的!

时间: 2024-08-25 19:18:31

模板:tarjan缩点+重新建边+最短路的相关文章

[模板]tarjan缩点+拓扑排序

题目:给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 题目简述:先tarjan缩点,再从入度为零处进行一次拓扑排序,求最长路即可,话说拓扑排序求最长路真方便... 注意: 要明确拓扑的写法,要用栈写最优. 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define man 100010 4 inline i

【模板】缩点 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^

图论算法-Tarjan模板 【缩点;割顶;双连通分量】

图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; bool ins[100010]; int col[100010];//记录每个点所属强连通分量(即染色) vector<int> map[100010]; stack<int> st; int tot;//时间戳 int colnum;//记录强连通分量个数 void tarjan(

P3387 【模板】缩点 题解 (Tarjan)

题目链接 P3387 [模板]缩点 解题思路 这几天搞图论,好有趣hhh,多写几篇博客. 上次学\(Tarjan\)求割点,这次缩点. 思路大概是多一个栈和染色的步骤,每次\(Tarjan\)的时候把点入栈,如果某个点(比较像割点但不完全是)的\(DFS\)子树都搜不到它祖宗,那么接下来进行的遍历操作必然与该点不能形成强连通分量,所以可以遇到\(low[p]==dfn[p]\)的点就把栈里面的东西全弹出来,染色表示这是一个强连通分量. 对于这个题,强连通分量之间再进行连边,记忆化搜索(类似树形D

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

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

Tarjan缩点模板 (洛谷P3387)

题目背景 缩点+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^

初涉tarjan缩点

tarjan缩点:口胡过好多题,不过从来没写过…… 什么是缩点 tarjan和Kosaraju.Gabow算法一样,是为了求有向图中的强连通分量.因为有向图中大多数情况下会有环存在,而有环是一个不甚好的性质.如果把有向图里的所有强连通分量都看作是一个点(缩点),则原图就会变成一个DAG——DAG是一个好东西. 什么是tarjan缩点 tarjan算法网上大多有介绍,我也在之前看过多次,不过从未写过,这里不再介绍. 今天把核心代码重新看了一遍,终于深入理解了其算法.那么就不妨在这里直接放上代码.

强连通分量tarjan缩点——POJ2186 Popular Cows

这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定意味着v可达u.    相互可达则属于同一个强连通分量    (Strongly Connected Component, SCC) §有向图和它的转置的强连通分量相同 §所有SCC构成一个DAG(有向无环图) dfn[u]为节点u搜索的次序编号(时间戳),即首次访问u的时间 low[u]为u或u的

【BZOJ-1924】所驼门王的宝藏 Tarjan缩点(+拓扑排序) + 拓扑图DP

1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 787  Solved: 318[Submit][Status][Discuss] Description Input 第一行给出三个正整数 N, R, C. 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设在位于第 xi行第yi列的藏宝宫室,类型为 Ti.Ti是一个1~3间的整数, 1表示可以传送到第 xi行任意