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

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #define maxn 100010
  6 using namespace std;
  7 inline int read()
  8 {
  9     int x=0,f=1;
 10     char ch=getchar();
 11     while(ch<‘0‘||ch>‘9‘)
 12     {
 13         if(ch==‘-‘) f=-1;
 14         ch=getchar();
 15     }
 16     while(ch>=‘0‘&&ch<=‘9‘)
 17     {
 18         x=x*10+ch-‘0‘;
 19         ch=getchar();
 20     }
 21     return x*f;
 22 }
 23 int head[maxn],ecnt,vis[maxn],dis[maxn],low[maxn],dfn[maxn],stk[maxn],tot,colortm,color[maxn],top,f[maxn],ans,w[maxn],x[maxn],y[maxn],n,m;
 24 struct edge
 25 {
 26     int u,v,next;
 27 }E[maxn];
 28 void add(int u,int v)
 29 {
 30     E[++ecnt].u=u;
 31     E[ecnt].v=v;
 32     E[ecnt].next=head[u];
 33     head[u]=ecnt;
 34 }
 35 void tarjan(int u)
 36 {
 37     vis[u]=1;
 38     stk[++top]=u;
 39     low[u]=dfn[u]=++tot;
 40     for(int i=head[u];i;i=E[i].next)
 41     {
 42         int v=E[i].v;
 43         if(!dfn[v])
 44         {
 45             tarjan(v);
 46             low[u]=min(low[u],low[v]);
 47         }
 48         else if(vis[v]) low[u]=min(low[u],dfn[v]);
 49     }
 50     if(dfn[u]==low[u])
 51     {
 52         ++colortm;
 53         vis[u]=0;
 54         while(stk[top+1]!=u)
 55         {
 56             vis[stk[top]]=0;
 57             color[stk[top]]=colortm;
 58             f[colortm]+=w[stk[top]];
 59             ans=max(ans,f[colortm]);
 60             top--;
 61         }
 62     }
 63 }
 64 void bfs(int x)
 65 {
 66     memset(vis,0,sizeof(vis));
 67     memset(dis,0,sizeof(dis));
 68     queue<int>q;
 69     q.push(x);
 70     vis[x]=1;
 71     dis[x]=f[x];
 72     while(!q.empty())
 73     {
 74         int u=q.front();
 75         for(int i=head[u];i;i=E[i].next)
 76         {
 77             int v=E[i].v;
 78             if(dis[v]<dis[u]+f[v])
 79             {
 80                 dis[v]=dis[u]+f[v];
 81                 if(!vis[v])
 82                 {
 83                     q.push(v);
 84                     vis[v]=1;
 85                 }
 86              }
 87         }
 88         q.pop();
 89         vis[u]=0;
 90     }
 91     for(int i=1;i<=colortm;++i) ans=max(ans,dis[i]);
 92 }
 93 int main()
 94 {
 95     n=read();m=read();
 96     for(int i=1;i<=n;++i) w[i]=read();
 97     for(int i=1;i<=m;++i)
 98     {
 99         int a=read(),b=read();
100         add(a,b);
101         x[i]=a;
102         y[i]=b;
103     }
104     for(int i=1;i<=n;++i)
105     {
106         if(!dfn[i]) tarjan(i);
107     }
108     memset(head,0,sizeof(head));
109     memset(E,0,sizeof(E));
110     ecnt=0;
111     for(int i=1;i<=m;++i)
112     {
113         if(color[x[i]]!=color[y[i]])
114         {
115             add(color[x[i]],color[y[i]]);
116         }
117     }
118
119     for(int i=1;i<=colortm;++i)
120         bfs(i);
121     printf("%d\n",ans);
122     return 0;
123  } 

原文地址:https://www.cnblogs.com/mljkw-gsry/p/8323944.html

时间: 2024-08-03 08:35:30

Tarjan缩点模板 (洛谷P3387)的相关文章

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

洛谷 P3387 【模板】缩点

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

【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.寻找与当前点

【模板】LIS模板 洛谷P1091 [NOIP2004提高组]合唱队形 [2017年4月计划 动态规划11]

以题写模板. 写了两个:n^2版本与nlogn版本 P1091 合唱队形 题目描述 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K). 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形. 输入输出格

缩点(洛谷3387)——不会写DP 的我只好来了个SPFA

我刚开始也不知道为什么就想到肯定是缩了点后把一个新点(原图中的强连通分量)的权值赋为它所含的所有点的权值之和,没有想着去推,纯粹是题目的名字启发我这么去干的……之后用SPFA 求最大路径,然而我连的全是无向边,导致答案错误了四个点,发现错误后觉得,之前的代码居然还对了六个点才是最让人震惊的. 1 #include<queue> 2 #include<stack> 3 #include<vector> 4 #include<iostream> 5 #inclu

洛谷P3387 缩点模板

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

最小费用最大流基础模板(洛谷3381)

如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向P3381 [模板]最小费用最大流边的个数.源点序号.汇点序号. 接下来M行每行包含四个正整数ui.vi.wi.fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi. 输出格式: 一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用. 输入样例#1:

[模板]洛谷T3373 线段树 模板2

此题相对于模板一,加了个区间乘,于是在模板一的基础上需要多开个数组(记录乘法懒标记).多写个函数(区间乘),还有要把懒标记下放函数做些修改. 变量定义: sum[]:线段树节点对应区间的元素总和: addv[]:线段树节点对应区间的所有元素待加的值(懒标记),初值全部设为0: mulv[]:线段树节点对应区间的所有元素待乘的值(懒标记),初值全部设为1. 过程说明: 建树(Build): 同模板一... 懒标记下放(Push_down): 原理解释: 1.当对某区间执行加法操作时,由于加法优先级

受欢迎的牛(Tarjan缩点模板)

#include<cstdio> #include<cstring> #include<stack> using namespace std; int n,m,head[10005],vis[10005],dfn[10005], low[10005],color[10005],num[10005],out[10005]; int sum,cnt,tot,jia,ans; stack<int> s; struct edge{ int v,next; }e[50