本文同步发表于https://www.zybuluo.com/Gary-Ying/note/1235385

题目描述

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

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

输入输出格式

输入格式:

第一行,n,m

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

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

输出格式:

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

样例输入

2 2
1 1
1 2
2 1

样例输出

2

数据范围

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

题解

算法

正如题目名称所说,这道题需要使用缩点的技巧,那么这是为什么呢?

我们可以把一个强连通分量近似看作一个环,由于这道题中边权只能取1次,每个点和每条边可以经过多次,所以一个强连通分量中的点一定可以全部取到。并且强连通分量中各点的出边可以看做是同一点的出边。于是我们可以高兴地说:

这道题中一个强连通分量相当于一个点,我们可以缩点了!

缩点以后图就变成了DAG(有向无环图),对于有向无环图,注定是要与拓扑排序为伴的。DP转移方程如下:(dp[v]表示走到v点的最大路径长度,a[v]表示v这个强连通分量中所有点的点权之和)

\[dp[v]=max\{dp[u]+a[v]\}
\]

转移的条件是存在边(u,v)。用拓扑排序转移DP即可(避免后效性,保证处理v的转移时u的转移已经处理)。

易错点(其实就是我错的)

1、第37行:if (vis[v])low[u] = min(low[u], dfn[v]);

2、

for (int i = 1; i <= n; ++i)
if (!dfn[i])tarjan(i);

3、 第84行:addedge1(color[i],color[v]);

Code

#include<iostream>
#include<cstdio>
using namespace std; const int maxn = 10007;
const int maxm = 100007;
int n, m;
int c[maxn], a[maxn];
int edgenum, head[maxn], Next[maxm], vet[maxm];
int edgenum1, head1[maxn], Next1[maxm], vet1[maxm];
int dfn[maxn], low[maxn], stamp;
int stack[maxn], top, color[maxn], num;
int init[maxn];
bool vis[maxn]; void addedge(int u, int v){
++edgenum;
vet[edgenum] = v;
Next[edgenum] = head[u];
head[u] = edgenum;
}
void addedge1(int u, int v){
++edgenum1;
vet1[edgenum1] = v;
Next1[edgenum1] = head1[u];
head1[u] = edgenum1;
++init[v];
}
void tarjan(int u){
dfn[u] = low[u] = ++stamp;
stack[++top] = u; vis[u] = true;
for (int e = head[u]; e; e = Next[e]){
int v = vet[e];
if (!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
}else if (vis[v])low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]){
color[u] = ++num; vis[u] = false;
while (stack[top] != u) vis[stack[top]] = false, color[stack[top--]] = num;
--top;
}
} void tp(){
int head = 0, tail = -1, que[maxn], dp[maxn];
for (int i = 1; i <= num; ++i) {
if (init[i] == 0){
que[++tail] = i;
}
dp[i] = a[i];
}
while (head <= tail){
int u = que[head]; ++head;
for (int e = head1[u]; e; e = Next1[e]){
int v = vet1[e];
--init[v];
if (init[v] == 0) que[++tail] = v;
dp[v] = max(dp[v], dp[u] + a[v]);
}
}
int ans = 0;
for (int i = 1; i <= num; ++i)
if (dp[i] > ans) ans = dp[i];
printf("%d\n", ans);
} int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", &c[i]);
for (int i = 1; i <= m; ++i){
int u, v;
scanf("%d%d", &u, &v);
addedge(u,v);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i])tarjan(i);
for (int i = 1; i <= n; ++i){
for (int e = head[i]; e; e = Next[e]){
int v = vet[e];
if (color[i]!=color[v]){
addedge1(color[i],color[v]);
}
}
a[color[i]] += c[i];
}
tp();
return 0;
}

【Luogu3381】【模板】缩点的更多相关文章

  1. Tarjan+topsort(DP)【P3387】 [模板]缩点

    Description 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次 ...

  2. Tarjan的缩点&&割点概述

    What is Tarjan? Tarjan,是一种用来解决图的联通性的一种有效途径,它的一般俗称叫做:缩点.我们首先来设想一下: 如果我们有一个图,其中A,B,C构成一个环,那么我们在某种条件下,如 ...

  3. Cogs 1298.通讯问题

    1298.通讯问题 ★ 输入文件:jdltt.in 输出文件:jdltt.out 简单对比 时间限制:1 s 内存限制:128 MB [题目描述] 一个篮球队有n个篮球队员,每个队员都有联系方式(如电 ...

  4. Ural 1382 2SAT

    ural1382 直接套用 2SAT模板 缩点 拓扑排序... #include<iostream> #include<cstdio> #include<cstdlib& ...

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

    图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; ...

  6. Tarjan总结(缩点+割点(边)+双联通+LCA+相关模板)

    Tarjan求强连通分量 先来一波定义 强连通:有向图中A点可以到达B点,B点可以到达A点,则称为强连通 强连通分量:有向图的一个子图中,任意两个点可以相互到达,则称当前子图为图的强连通分量 强连通图 ...

  7. tarjan 缩点(模板)

    描述: 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 注:允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 思路: ...

  8. 洛谷P3387 【模板】缩点 题解

    背景 今天\(loj\)挂了,于是就有了闲情雅致来刷\(luogu\) 题面 洛谷P3387 [模板]缩点传送门 题意 给定一个\(n\)个点\(m\)条边有向图,每个点有一个权值,求一条路径,使路径 ...

  9. 洛谷——P3387 【模板】缩点

    P3387 [模板]缩点 题目背景 缩点+DP 题目描述 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点, ...

随机推荐

  1. 解决bootstrap多模态框跳转时页面左移问题

    衍生问题暂未发现.... 忽略左右跳动视觉差 解决方法: 在bootstrap的js搜索padding-right,然后找到“+this.scrollbarWidth”,删掉即可.

  2. pycaffe简明文档

    pycaffe简明文档 by ChrisZZ, imzhuo@foxmail.com 2018年01月18日19:00:56 说明 caffe的python接口没有官方说明文档,例如查看一个函数的用法 ...

  3. PaperNotes Instance-Level Salient Object Segmentation

    title: PaperNotes Instance-Level Salient Object Segmentation comments: true date: 2017-12-20 13:53:1 ...

  4. Typora开启行内公式

    文件→偏好设置→Markdown,勾选内联公式,重启typora 输入$,按Esc键会自动在后面加上一个$,然后在这两个$之间输入公式.

  5. MySQl 查询性能优化相关

    0. 1.参考 提升网站访问速度的 SQL 查询优化技巧 缓存一切数据,读取内存而不是硬盘IO 如果你的服务器默认情况下没有使用MySQL查询缓存,那么你应该开启缓存.开启缓存意味着MySQL 会把所 ...

  6. windows docker常用命令

    关键词 示例 作用 attach sudo docker run -itd ubuntu:14.04 /bin/bash 进入容器 exec docker exec -it mysql bash 在容 ...

  7. BZOJ3052/UOJ#58 [wc2013]糖果公园 莫队 带修莫队 树上莫队

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ3052.html 题目传送门 - BZOJ3052 题目传送门 - UOJ#58 题意 给定一棵树,有 ...

  8. BZOJ3393 [Usaco2009 Jan]Laserphones 激光通讯 BFS

    原文链接http://www.cnblogs.com/zhouzhendong/p/8371735.html 题目传送门 - BZOJ3393 题意概括 直接看原题的翻译吧,很容易懂的. 题解 我不知 ...

  9. Java中用Scanner扫描控制台输入时的一个小问题

    package com.hxl; import java.util.Scanner; public class Test { public static void main(String[] args ...

  10. Spring Data Redis实现消息队列——发布/订阅模式

    一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式.利用redis这两种场景的消息队列都能够实现. 定义:生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列, ...