标签:scan 模板 struct bool while ace 重复 top amp
缩点+DP
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
n<=10^4,m<=10^5,0<=点权<=1000
算法:Tarjan缩点+DAGdp
#include<bits/stdc++.h> using namespace std; struct edge{ int nw,nxt,mark; }pre[100010]; int n,m,idx,cnt; int dfn[10010],low[10010]; int in[10010],v[10010],fa[10010]; int head[10010]; bool used[10010]; int stk[10010],p; int ans=0; void add (int x,int y,int cnt) { pre[cnt].nw=x; pre[cnt].mark=head[x]; pre[cnt].nxt=y; head[x]=cnt; } void tarjan (int u) { dfn[u]=low[u]=++idx; stk[++p]=u; used[u]=1; for (int i=head[u];i!=0;i=pre[i].mark) { int nx=pre[i].nxt; if (!dfn[nx]) { tarjan (nx); low[u]=min (low[u],low[nx]); } else if (used[nx]) low[u]=min (low[u],dfn[nx]); } if (low[u]==dfn[u]) { do{ v[u]+=v[stk[p]]; fa[stk[p]]=u; used[stk[p]]=0; p--; }while (stk[p+1]!=u); v[u]>>=1; } } int topo () { int dis[10010]; queue<int>q; for (int i=1;i<=n;i++) if (fa[i]==i) { dis[i]=v[i]; if (!in[i]) q.push(i); } while (!q.empty()) { int Now=q.front(); for (int i=head[Now];i!=0;i=pre[i].mark) { int Nxt=pre[i].nxt; dis[Nxt]=max (dis[Nxt],dis[Now]+v[Nxt]); in[Nxt]--; if (!in[Nxt]) q.push (Nxt); } q.pop(); } int maxx=0; for (int i=1;i<=n;i++) if (fa[i]==i) maxx=max (maxx,dis[i]); return maxx; } int main() { memset (in,0,sizeof (in)); memset (used,0,sizeof (used)); memset (dfn,0,sizeof(dfn)); memset (head,0,sizeof (head)); scanf ("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf ("%d",&v[i]); for (int i=1;i<=m;i++) { int a,b; scanf ("%d%d",&a,&b); add (a,b,i); } for (int i=1;i<=n;i++) if (!dfn[i]) tarjan (i); memset (head,0,sizeof (head)); for (int i=1;i<=m;i++) { int Now=fa[pre[i].nw]; int Nxt=fa[pre[i].nxt]; if (Now!=Nxt) { add (Now,Nxt,++cnt); in[Nxt]++; } } printf ("%d",topo ()); return 0; }
标签:scan 模板 struct bool while ace 重复 top amp
原文地址:https://www.cnblogs.com/xiongchongwen/p/11156581.html