洛谷P3387 缩点模板

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

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

因为可以重复经过点,所以一个点所在的强联通分量必定可以到达。所以直接缩点即可。

缩点之后,我们要让权值最大化,必须从入度为0的点开始搜。因为这是DAG,入度不为零的点的最后祖先必定是入度为零的点。由于这道题没有负数权值,从入度为零的结点开始走肯定是一个最好的选择。然后DAG上跑动归即可。

  1 #include <cstdio>
  2 #include <cstring>
  3 using namespace std;
  4
  5 const int maxn=1e4+5, maxm=1e5+5;
  6
  7 class Graph{
  8 public:
  9     //为什么用嵌套类,因为声明很烦。。并不是访问问题的原因。
 10     //如果类的成员不是static的话,内部类是不知道获取哪个外部类对象的。。
 11     //所以无论是把类放在里面还是外面,访问都有问题。
 12     //另外,把private放在下面,应该是因为嵌套类类必须完全声明才能使用吧。。
 13     //我也不大清楚。。先这么用着,以后再去翻c++ primer。
 14     class Edge{
 15     private:
 16         Graph *belong;
 17     public:
 18         int v, to, next; //感觉这里还是不优雅。。
 19         Edge(){}
 20         Edge(Graph& g, int x, int y, int z){
 21             belong=&g;
 22             to=x, v=y, next=z;
 23         }
 24         Edge operator ++(){ //不能打引用!
 25             *this=belong->edge[next]; //因为有自增运算,这个edge不能等同于图中的!
 26             return *this;
 27         }
 28         int operator *(){
 29             return to;
 30         }
 31     };
 32     Graph(){}
 33     void addedge(int x, int y, int v){
 34         ++cntedge;
 35         edge[cntedge]=Edge(*this, y, v, fir[x]);
 36         fir[x]=cntedge;
 37         return;
 38     }
 39     Edge get_link(int x){ //这里改成begin较好
 40         return edge[fir[x]];
 41     }
 42 private:
 43     int cntedge, fir[maxn];
 44     Edge edge[maxm];
 45 };
 46
 47 int n, m, cnttime, cntscc;
 48 int v[maxn], visit[maxn], time[maxn];
 49 int scc[maxn], vofscc[maxn], in[maxn], ans[maxn];
 50 Graph g, g_t, g_scc;
 51
 52 void dfs(int now, int flag){
 53     visit[now]=1;
 54     Graph::Edge e=g.get_link(now);
 55     if (flag==2) e=g_t.get_link(now);
 56     int to=*e;
 57     while (to){
 58         if (!visit[to]) dfs(to, flag);
 59         to=*(++e);
 60     }
 61     if (flag==1){
 62         ++cnttime;
 63         time[cnttime]=now;
 64     }
 65     if (flag==2) {
 66         scc[now]=cntscc;
 67         vofscc[cntscc]+=v[now];
 68     }
 69     return;
 70 }
 71
 72 void dfs2(int x){
 73     Graph::Edge e=g.get_link(x);
 74     int to=*e;
 75     visit[x]=1;
 76     while (to){
 77         if (scc[to]!=scc[x]){
 78             g_scc.addedge(scc[x], scc[to], v[scc[to]]);
 79             ++in[scc[to]];
 80         }
 81         if (!visit[to])
 82             dfs2(to);
 83         to=*(++e);
 84     }
 85     return;
 86 }
 87
 88 void dfs3(int x){
 89     Graph::Edge e=g_scc.get_link(x);
 90     int to=*e;
 91     visit[x]=1;
 92     ans[x]=vofscc[x];
 93     while (to){
 94         if (!visit[to]) dfs3(to);
 95         if (ans[to]+vofscc[x]>ans[x])
 96             ans[x]=ans[to]+vofscc[x];
 97         to=*(++e);
 98     }
 99     return;
100 }
101
102 int main(){
103     scanf("%d%d", &n, &m);
104     for (int i=1; i<=n; ++i)
105         scanf("%d", &v[i]);
106     int x, y;
107     for (int i=1; i<=m; ++i){
108         scanf("%d%d", &x, &y);
109         g.addedge(x, y, 1);
110         g_t.addedge(y, x, 1);
111     } //建图
112     for (int i=1; i<=n; ++i)
113         if (!visit[i]) dfs(i, 1); //时间戳
114     memset(visit, 0, sizeof(visit));
115     for (int i=n; i>0; --i){
116         if (!visit[time[i]]){
117             ++cntscc;
118             dfs(time[i], 2);
119         }
120     } //求强联通分量
121     memset(visit, 0, sizeof(visit));
122     for (int i=1; i<=n; ++i){
123         if (!visit[i]) dfs2(i);
124     } //缩点
125     for (int i=1; i<=cntscc; ++i)
126         if (!in[i]) g_scc.addedge(0, i, vofscc[i]); //超级源汇
127     memset(visit, 0, sizeof(visit));
128     dfs3(0); //树形dp
129     printf("%d\n", ans[0]);
130     return 0;
131 }
时间: 2024-11-05 02:25:16

洛谷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: 复制

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^

【洛谷P3379】【模板】最近公共祖先(LCA)

题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入样例#1: 5 5 4 3 1 2 4 5

洛谷P3381——费用流模板题

嗯..随便刷了一道费用流的模板题....来练练手. #include<iostream> #include<cstdio> #include<cstring> using namespace std; int h[5210],d[5210],used[5210],que[100010],last[5210]; int k=1,INF=0x7fffffff,ans1=0,ans2=0; inline int read(){ int t=1,num=0; char c=ge

【原创】洛谷 LUOGU P3373 【模板】线段树2

P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含三个整数N.M.P,分别表示该数列数字的个数.操作的总个数和模数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k 操作2: 格式:2 x

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

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

洛谷试炼场 普及常见模板

对没错我就是在水博 P3366 [模板]最小生成树 kruskal: P3367 [模板]并查集 1 /*by SilverN*/ 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 using namespace std; 8 const int mxn=210000; 9 int read()

【原创】洛谷 LUOGU P3379 【模板】最近公共祖先(LCA) -&gt; 倍增

P3379 [模板]最近公共祖先(LCA) 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入