题解 P4742 【[Wind Festival]Running In The Sky】

P4742 [Wind Festival]Running In The Sky

题目大意:

给一张有向图,求一条点权最大的路径,和这条路径上的最大点权。

solution:

我们考虑将原图缩点,然后在新的 \(\text{DAG}\) 上拓扑排序进行 \(\text{DP}\)

我们设两个状态: \(f1[\,i\,]\)\(f2[\,i\,]\) 分别表示走到 \(i\) 的最大路径和与最大点权,思考一下如何转移:

设从 \(x\) 走到 \(y\) ,每个强连通分量的点权和为 \(val[\,i\,]\) ,最大点权为 \(mx[\,i\,]\)

  1. 如果:\(f1[\,y\,]<f1[\,x\,]+val[\,y\,]\),则有:

\[f1[\,y\,]=f1[\,x\,]+val[\,y\,],f2[\,y\,]= \max \{f2[\,x\,],mx[\,y\,]\} \]

  1. 如果:\(f1[\,y\,]==f1[\,x\,]+val[\,y\,]\),则有:

\[f2[\,y\,]=\max\{f2[\,x\,],f2[\,y\,]\} \]

考虑答案统计:
\(ans1\)\(ans2\)\(f1\)\(f2\) 同义;

  1. \(ans1<f1[\,x\,]\) ,则有:

\[ans1=f1[\,x\,],ans2=f2[\,x\,] \]

  1. \(ans1==f1[\,x\,]\) ,则有:

\[ans2= \max\{ans2,f2[\,x\,]\} \]

细节处理:

代码
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=2e5+5,M=5e5+5;
int hd[N],nt[M],fr[M],to[M],cnt;
inline void tian(int x,int y){
	to[++cnt]=y,fr[cnt]=x,nt[cnt]=hd[x],hd[x]=cnt;
}
int dfn[N],low[N],val[N],mx[N],of[N],chuo,scc;
int st[N],top; bool vis[N];
int a[N];
void dfs(int x){//模板
	dfn[x]=low[x]=++chuo;
	st[++top]=x,vis[x]=1;
	for(int i=hd[x];i;i=nt[i]){
		int y=to[i];
		if(!dfn[y]){
			dfs(y);
			low[x]=min(low[x],low[y]);
		}
		else if(vis[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		scc++;
		while(1){
			int y=st[top--];
			val[scc]+=a[y],
			mx[scc]=max(mx[scc],a[y]);
			of[y]=scc,vis[y]=0;
			if(x==y) break;
		}
	}
}
int nhd[N],nnt[M],nto[M],ncnt,du[N];
inline void ntian(int x,int y){
	nto[++ncnt]=y,nnt[ncnt]=nhd[x],nhd[x]=ncnt;
	du[y]++;
}
inline void suo(){
	for(int i=1;i<=cnt;i++){
		int x=of[fr[i]],y=of[to[i]];
		if(x!=y) ntian(x,y);
	}
}
int f1[N],f2[N];
int ans1,ans2;
queue<int> q;
inline void topo(){
	for(int i=1;i<=scc;i++){
		f1[i]=val[i],f2[i]=mx[i];
		if(!du[i]) q.push(i);
	}
	while(q.size()){
		int x=q.front();q.pop();
		if(f1[x]>ans1) ans1=f1[x],ans2=f2[x];
		else if(f1[x]==ans1) ans2=max(ans2,f2[x]);
		for(int i=nhd[x];i;i=nnt[i]){
			int y=nto[i];
			if(f1[y]<f1[x]+val[y]){
				f1[y]=f1[x]+val[y];
				f2[y]=max(f2[x],mx[y]);
			}
			else if(f1[y]==f1[x]+val[y])
				f2[y]=max(f2[x],f2[y]);
			if(--du[y]==0) q.push(y);
		}
	}
}
int main(){
	int n,m; scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		tian(x,y);//连边
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i]) dfs(i);//tarjin
	suo();//缩点
	topo();//拓扑排序+DP
	printf("%d %d",ans1,ans2);
	return 0;
}

End

posted @ 2021-08-04 09:15  Mr_think  阅读(47)  评论(0)    收藏  举报