洛谷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
题解:模板缩点,马上就要NOIP了,当作复习一下。
缩点的思想是找到一个环,把这个环缩成一个点。若原图联通,则缩点并不影响环外的连通性。
因此对于这题,我们把环缩成点时,环内所有的点的权值和为新点的权值,然后从所有入度为零的点作为起点跑一边SPFA就能求得单向路径上的最大值。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #include<stack> 6 using namespace std; 7 const int N=10005,M=100005; 8 int n,m,x,y,cnt,tot,ans,a[N],p[N],fro[M],to[M],nxt[M],head[N]; 9 int Cnt,To[M],Nxt[M],Head[M],deg[N],dfn[N],low[N],grp[N],dis[N]; 10 bool vis[N],mark[N]; 11 queue<int> Q; 12 stack<int> S; 13 void add(int u,int v){ 14 fro[++cnt]=u; to[cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; 15 } 16 void Add(int u,int v){ 17 To[++Cnt]=v; Nxt[Cnt]=Head[u]; Head[u]=Cnt; 18 } 19 void Tarjan(int u) 20 { 21 vis[u]=mark[u]=1; S.push(u); dfn[u]=low[u]=++tot; 22 for (int i=head[u];i;i=nxt[i]) 23 { 24 int v=to[i]; 25 if (!dfn[v]) 26 { 27 Tarjan(v); 28 low[u]=min(low[u],low[v]); 29 } 30 if (mark[v]) low[u]=min(low[u],dfn[v]); 31 } 32 if (low[u]==dfn[u]) 33 { 34 ++tot; 35 int x=S.top(); 36 while (x!=u) 37 { 38 grp[x]=tot; 39 p[tot]+=a[x]; 40 mark[x]=0; 41 S.pop(); 42 x=S.top(); 43 } 44 grp[x]=tot; 45 p[tot]+=a[x]; 46 mark[x]=0; 47 S.pop(); 48 } 49 } 50 void SPFA(int x) 51 { 52 ans=max(ans,p[x]); 53 memset(vis,0,sizeof(vis)); 54 memset(dis,0,sizeof(dis)); 55 Q.push(x); dis[x]=p[x]; vis[x]=1; 56 while (!Q.empty()) 57 { 58 int u=Q.front(); Q.pop(); vis[u]=0; 59 for (int i=Head[u];i;i=Nxt[i]) 60 { 61 int v=To[i]; 62 if (dis[u]+p[v]>dis[v]) 63 { 64 dis[v]=dis[u]+p[v]; 65 ans=max(ans,dis[v]); 66 if (!vis[v]) vis[v]=1,Q.push(v); 67 } 68 } 69 } 70 } 71 int main() 72 { 73 scanf("%d%d",&n,&m); 74 for (int i=1;i<=n;++i) scanf("%d",&a[i]); 75 for (int i=1;i<=m;++i) scanf("%d%d",&x,&y),add(x,y); 76 for (int i=1;i<=n;++i) if (!vis[i]) Tarjan(i); 77 for (int i=1;i<=m;++i) 78 { 79 if (grp[fro[i]]!=grp[to[i]]) 80 { 81 Add(grp[fro[i]],grp[to[i]]); 82 ++deg[grp[to[i]]]; 83 } 84 } 85 for (int i=1;i<=tot;++i) if (!deg[i]) SPFA(i); 86 printf("%d\n",ans); 87 return 0; 88 }
时间: 2024-10-10 23:14:56