您的位置:首页 > 其它

P3387 【模板】缩点

2017-11-25 19:55 232 查看

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

 

分析

缩点成有点无环图(树),然后拓扑+dp

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>

using namespace std;

const int N = 20100;
const int M = 200100;
struct Edge{
int to,nxt;
}e[M],s[M]; // 不要混用!!!
int head
,dfn
,low
,st
,bel
,h
;
int ru
,q[1000000],L,R,w
,sw
,ans
;
bool vis
;
int cnt,tn,tot,top,tot1;

inline char nc() {
static char buf[100000],*p1 = buf,*p2 = buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2) ? EOF : *p1++;
}
inline int read() {
int x = 0,f = 1;char ch = nc();
for (; ch<'0'||ch>'9'; ch = nc())
if (ch=='-') f = -1;
for (; ch>='0'&&ch<='9'; ch = nc())
x = x*10+ch-'0';
return x * f;
}

inline void add_edge(int u,int v) {
e[++tot].to = v,e[tot].nxt = head[u],head[u] = tot;
}
inline void add(int u,int v) {
s[++tot1].to = v,s[tot1].nxt = h[u],h[u] = tot1; // 不要写成e
}
void tarjan(int u) {
dfn[u] = low[u] = ++tn;
st[++top] = u;
vis[u] = true;
for (int i=head[u]; i; i=e[i].nxt) {
int v = e[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if (vis[v])
low[u] = min(low[u],dfn[v]);
}
if (low[u]==dfn[u]) {
++cnt;
do {
vis[st[top]] = false;
bel[st[top]] = cnt;
sw[cnt] += w[st[top]];
top--;
} while (st[top+1] != u);
}
}

int main() {

int n = read(),m = read();
for (int i=1; i<=n; ++i)
w[i] = read();
for (int i=1; i<=m; ++i) {
add_edge(read(),read());
}
for (int i=1; i<=n; ++i)
if (!dfn[i]) tarjan(i);
for (int u=1; u<=n; ++u) {
for (int i=head[u]; i; i=e[i].nxt) {
int v = e[i].to;
if (bel[u]!=bel[v])
add(bel[u],bel[v]),ru[bel[v]]++;
}
}
L = 1,R = 0;
for (int i=1; i<=cnt; ++i) {
if (!ru[i]) q[++R] = i,ans[i] = sw[i];
}

while (L <= R) {
int u = q[L++];
for (int i=h[u]; i; i=s[i].nxt) {
int v = s[i].to;
ans[v] = max(ans[u]+sw[v],ans[v]);
ru[v] --;
if (ru[v]==0) q[++R] = v; // 拓扑!!!不是bfs

}
}
int mx = 0;
for (int i=1; i<=cnt; ++i) {
mx = max(mx,ans[i]);
}
printf("%d",mx);
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: