洛谷P3387 【模板】缩点

洛谷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

题解:模板缩点,马上就要NOIP了,当作复习一下。

缩点的思想是找到一个环,把这个环缩成一个点。若原图联通,则缩点并不影响环外的连通性。

因此对于这题,我们把环缩成点时,环内所有的点的权值和为新点的权值,然后从所有入度为零的点作为起点跑一边SPFA就能求得单向路径上的最大值。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 #include<stack>
 6 using namespace std;
 7 const int N=10005,M=100005;
 8 int n,m,x,y,cnt,tot,ans,a[N],p[N],fro[M],to[M],nxt[M],head[N];
 9 int Cnt,To[M],Nxt[M],Head[M],deg[N],dfn[N],low[N],grp[N],dis[N];
10 bool vis[N],mark[N];
11 queue<int> Q;
12 stack<int> S;
13 void add(int u,int v){
14     fro[++cnt]=u; to[cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
15 }
16 void Add(int u,int v){
17     To[++Cnt]=v; Nxt[Cnt]=Head[u]; Head[u]=Cnt;
18 }
19 void Tarjan(int u)
20 {
21     vis[u]=mark[u]=1; S.push(u); dfn[u]=low[u]=++tot;
22     for (int i=head[u];i;i=nxt[i])
23     {
24         int v=to[i];
25         if (!dfn[v])
26         {
27             Tarjan(v);
28             low[u]=min(low[u],low[v]);
29         }
30         if (mark[v]) low[u]=min(low[u],dfn[v]);
31     }
32     if (low[u]==dfn[u])
33     {
34         ++tot;
35         int x=S.top();
36         while (x!=u)
37         {
38             grp[x]=tot;
39             p[tot]+=a[x];
40             mark[x]=0;
41             S.pop();
42             x=S.top();
43         }
44         grp[x]=tot;
45         p[tot]+=a[x];
46         mark[x]=0;
47         S.pop();
48     }
49 }
50 void SPFA(int x)
51 {
52     ans=max(ans,p[x]);
53     memset(vis,0,sizeof(vis));
54     memset(dis,0,sizeof(dis));
55     Q.push(x); dis[x]=p[x]; vis[x]=1;
56     while (!Q.empty())
57     {
58         int u=Q.front(); Q.pop(); vis[u]=0;
59         for (int i=Head[u];i;i=Nxt[i])
60         {
61             int v=To[i];
62             if (dis[u]+p[v]>dis[v])
63             {
64                 dis[v]=dis[u]+p[v];
65                 ans=max(ans,dis[v]);
66                 if (!vis[v]) vis[v]=1,Q.push(v);
67             }
68         }
69     }
70 }
71 int main()
72 {
73     scanf("%d%d",&n,&m);
74     for (int i=1;i<=n;++i) scanf("%d",&a[i]);
75     for (int i=1;i<=m;++i) scanf("%d%d",&x,&y),add(x,y);
76     for (int i=1;i<=n;++i) if (!vis[i]) Tarjan(i);
77     for (int i=1;i<=m;++i)
78     {
79         if (grp[fro[i]]!=grp[to[i]])
80         {
81             Add(grp[fro[i]],grp[to[i]]);
82             ++deg[grp[to[i]]];
83         }
84     }
85     for (int i=1;i<=tot;++i) if (!deg[i]) SPFA(i);
86     printf("%d\n",ans);
87     return 0;
88 }

时间: 2024-10-10 23:14:56

洛谷P3387 【模板】缩点的相关文章

【C++】最近公共祖先LCA(Tarjan离线算法)&amp;&amp; 洛谷P3379LCA模板

1.前言 首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为). 2.思想 下面详细介绍一下Tarjan算法的思想: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5.寻找与当前点

AC自动机(附洛谷P3769模板题)

首先,介绍一下AC自动机(Aho-Corasick automaton),是一种在一个文本串中寻找每一个已给出的模式串的高效算法. 在学习AC自动机之前,你需要先学习Trie树和KMP算法,因为AC自动机正式利用并结合了两者的思想. 说到实际的不同,其实AC自动机只是在Trie树上引入了一个类似KMP中next数组的东西叫做Fail指针. 对于每一个节点,Fail指针指向该节点所代表的字符串中,次长的.在Trie树中存在的后缀(因为最长的在Trie树种存在的后缀就是其本身)所代表的节点. 举例:

洛谷P3375 [模板]KMP字符串匹配

To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果你不知道这是什么意思也不要问,去百度搜[kmp算法]学习一下就知道了. 输入输出格式 输入格式: 第一行为一个字符串,即为s1(仅包含大写字母) 第二行为一个字符串,即为s2(仅包含大写字母) 输出格式: 若干行,每行包含一个整数,表示s2在s1中出现的位置 接下来1行,包括length(s2)个整

洛谷.3803.[模板]多项式乘法(FFT)

题目链接:洛谷.LOJ. FFT相关:快速傅里叶变换(FFT)详解.FFT总结.从多项式乘法到快速傅里叶变换. #include <cmath> #include <cctype> #include <cstdio> #include <algorithm> #define gc() getchar() const int N=1e6+5; const double PI=acos(-1); int n,m; struct Complex { double

洛谷.1919.[模板]A乘B Problem升级版(FFT)

题目链接:洛谷.BZOJ2179 //将乘数拆成 a0*10^n + a1*10^(n-1) + ... + a_n-1的形式 //可以发现多项式乘法就模拟了竖式乘法 所以用FFT即可 注意处理进位 //n位*n位最多就只有2n位了 //论putchar的速度..还是快的 #include <cmath> #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar

洛谷 P3387 【模板】缩点

P3387 [模板]缩点 题目背景 缩点+DP 题目描述 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 输入输出格式 输入格式: 第一行,n,m 第二行,n个整数,依次代表点权 第三至m+2行,每行两个整数u,v,表示u->v有一条有向边 输出格式: 共一行,最大的点权之和. 输入输出样例 输入样例#1: 复制 2 2 1 1 1 2 2 1 输出样例#1: 复制

洛谷P3387 缩点模板

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 因为可以重复经过点,所以一个点所在的强联通分量必定可以到达.所以直接缩点即可. 缩点之后,我们要让权值最大化,必须从入度为0的点开始搜.因为这是DAG,入度不为零的点的最后祖先必定是入度为零的点.由于这道题没有负数权值,从入度为零的结点开始走肯定是一个最好的选择.然后DAG上跑动归即可. 1 #include <cst

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^

洛谷 [P2483] [模板] k短路

人生中的第一道黑题... 其实就是k短路模板 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <cstdlib> #include <queue> using namespace std; const int MAXN=400005; int init(){ int