luoguP3387 【模板】缩点
www.cnblogs.com/shaokele/
luoguP3387 【模板】缩点##
Time Limit: 1 Sec
Memory Limit: 128 MBDescription###
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
Input###
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
Output###
共一行,最大的点权之和。
Sample Input###
2 2
1 1
1 2
2 1
Sample Output###
2
HINT###
n<=104,m<=105,点权<=1000
算法:Tarjan缩点+DAGdp
题目地址: luoguP3387 【模板】缩点
题目大意: 模板题
题解:
Tarjan缩点模板题
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e4+5,M=1e5+5;
int n,m,num,top,tot,ans,cnt1,cnt2;
int a[N],dfn[N],low[N],s[N],sum[N],col[N],f[N],in[N];
int last1[N],last2[N];
bool ins[N];
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct edge1{
int to,next;
}e1[M<<1];
struct edge2{
int to,next;
}e2[M<<1];
void add_edge1(int u,int v){
e1[++cnt1]=(edge1){v,last1[u]};last1[u]=cnt1;
}
void add_edge2(int u,int v){
e2[++cnt2]=(edge2){v,last2[u]};last2[u]=cnt2;
}
void Tarjan(int u){
dfn[u]=low[u]=++tot;
s[++top]=u;ins[u]=1;
for(int i=last1[u];i;i=e1[i].next){
int v=e1[i].to;
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
}else if(ins[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
num++;
while(s[top]!=u){
sum[num]+=a[s[top]];
col[s[top]]=num;
ins[s[top--]]=0;
}
sum[num]+=a[s[top]];
col[s[top]]=num;
ins[s[top--]]=0;
}
}
void dp(int u,int fa){
f[u]=sum[u];
for(int i=last2[u];i;i=e2[i].next){
int v=e2[i].to;
if(v==fa)continue;
dp(v,u);
f[u]=max(f[u],f[v]+sum[u]);
}
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
add_edge1(u,v);
}
for(int i=1;i<=n;i++)
if(dfn[i]==0)Tarjan(i);
for(int u=1;u<=n;u++)
for(int i=last1[u];i;i=e1[i].next){
int v=e1[i].to;
if(col[u]!=col[v]){
add_edge2(col[u],col[v]);
in[col[v]]++;
}
}
for(int i=1;i<=num;i++)
if(in[i]==0){
dp(i,0);
ans=max(ans,f[i]);
}
printf("%d\n",ans);
return 0;
}