洛谷 P3387 【模板】缩点
题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
输入输出样例
说明
n<=10^4,m<=10^5,|点权|<=1000 算法:Tarjan缩点+DAGdp
#include<map> #include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXN 100010 using namespace std; queue<int>que; map<int,int>ma[MAXN]; int n,m; int sumcol,ans; int tot,tot1,tim,top; int col[MAXN],into[MAXN]; int val[MAXN],cost[MAXN],dis[MAXN]; int to[MAXN],net[MAXN],head[MAXN]; int to1[MAXN],net1[MAXN],head1[MAXN]; int low[MAXN],dfn[MAXN],vis[MAXN],stack[MAXN],visstack[MAXN]; void add(int u,int v){ to[++tot]=v;net[tot]=head[u];head[u]=tot; } void add1(int u,int v){ to1[++tot1]=v;net1[tot1]=head1[u];head1[u]=tot1; } void tarjin(int now){ stack[++top]=now; low[now]=dfn[now]=++tim; vis[now]=1; visstack[now]=1; for(int i=head[now];i;i=net[i]) if(visstack[to[i]]) low[now]=min(low[now],dfn[to[i]]); else if(!vis[to[i]]){ tarjin(to[i]); low[now]=min(low[now],low[to[i]]); } if(dfn[now]==low[now]){ sumcol++; col[now]=sumcol; while(stack[top]!=now){ col[stack[top]]=sumcol; visstack[stack[top]]=0; top--; } visstack[now]=0; top--; } } void spfa(int s){ memset(dis,0,sizeof(dis)); memset(vis,0,sizeof(vis)); while(!que.empty()) que.pop(); dis[s]=cost[s]; vis[s]=1; que.push(s); while(!que.empty()){ int now=que.front(); que.pop(); vis[now]=0; for(int i=head1[now];i;i=net1[i]) if(dis[to1[i]]<dis[now]+cost[to1[i]]){ dis[to1[i]]=dis[now]+cost[to1[i]]; if(!vis[to1[i]]){ vis[to1[i]]=1; que.push(to1[i]); } } } for(int i=1;i<=sumcol;i++) ans=max(ans,dis[i]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&val[i]); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); } for(int i=1;i<=n;i++) if(!vis[i]) tarjin(i); for(int i=1;i<=n;i++) for(int j=head[i];j;j=net[j]) if(col[i]!=col[to[j]]) if(ma[col[i]].find(col[to[j]])==ma[col[i]].end()){ ma[col[i]][col[to[j]]]=1; into[col[to[j]]]++; add1(col[i],col[to[j]]); } for(int i=1;i<=n;i++) cost[col[i]]+=val[i]; for(int i=1;i<=sumcol;i++) if(!into[i]) spfa(i); cout<<ans; }
细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。
雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。