强连通分量模板+DAGdp 洛谷P3387

/*
	luogu P3387
	缩点(强连通分量)+DAGdp
	by sbn
	2018-2-11 
	algorithm:Kosaraju 
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
const int MAXN=1e5+11;
struct node{
	int x,y,nxt;
	node(){}
	node(int nx,int ny,int nnxt){
		x=nx;y=ny;nxt=nnxt;
	}
}E[MAXN*4],F[MAXN*4],S[MAXN*4];
//	E:原图 F:逆向图 S:缩点后的图 
int cnt,head[MAXN],headf[MAXN],cntf,cnts,heads[MAXN],dp[MAXN];
//cnt?和head?配套图使用
//dp表示从起点到这个点最大值 
int rec[MAXN],size,rd[MAXN]={0},w[MAXN],cd[MAXN];
//rec是dfs逆向图生成的dfs序列(见Kosaraju算法)
//size是序列大小
//rd,cd是缩点后各点的入度,出度
//w是原图中各个点的权值 
int ds[MAXN];
//ds是缩点后各个强连通分量的各点权值之和 
bool vis[MAXN];
//各种搜索用的标记 
inline void link(int x,int y){
	E[++cnt]=node(x,y,head[x]);
	head[x]=cnt;
	F[++cntf]=node(y,x,headf[y]);
	headf[y]=cntf;
}
int n,m,a,b,ans[MAXN],len[MAXN],q,num,ptd[MAXN];
//n是点的个数,m是边的个数,a,b是用于读入时的点
/*len,ans用于存生成的缩点 
	例如ans:1,2,3,4,5,6
		len:0,3,6
		表示123,456是两强连通分量 
*/ 
// q是dfs时的临时操作,表示ans下标 
//num是强连通分量的个数
//ptd是每个点对应的强连通分量是谁 
void dfsf(int u){
	vis[u]=1;
	for (int i=headf[u];i;i=F[i].nxt)
		if (!vis[F[i].y])	dfsf(F[i].y);
	size++;
	rec[size]=u;
	return;
}
//dfs逆序图,生成rec 
void dfs(int u){
	vis[u]=1;
	q++;
	ans[q]=u;
	ptd[u]=num;
	for (int i=head[u];i;i=E[i].nxt)
		if (!vis[E[i].y])
			dfs(E[i].y);
	return;
} 
//dfs正序图,生成ans,len 
inline void link_D(int x,int y){
	S[++cnts]=node(x,y,heads[x]);
	heads[x]=cnts;
}
//缩点后的链接 
inline void build(){
	for(int i=1;i<=n;i++)
		for (int j=head[i];j;j=E[j].nxt)
			if (ptd[i]!=ptd[E[j].y])
				{
					link_D(ptd[i],ptd[E[j].y]);
					rd[ptd[E[j].y]]++; 
					cd[ptd[i]]++;
				}
}
//缩点后建图 
void bfs(int u){
	queue<int> Q;
	Q.push(u);
	while (!Q.empty()){
		int k=Q.front();
		Q.pop();
		for (int i=heads[k];i;i=S[i].nxt)
		{	
			dp[S[i].y]=max(dp[S[i].y],dp[k]+ds[S[i].y]);
			Q.push(S[i].y);
		}
	}
}
//DAGdp部分,宽搜记忆化搜索 
int main(){
//	freopen("qltfl.txt","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)	scanf("%d",&w[i]);
	for (int i=1;i<=m;i++){
		scanf("%d%d",&a,&b);
		link(a,b);
	}
	memset(vis,0,sizeof(vis));
	for (int j=1;j<=n;j++)
		if (!vis[j])	dfsf(j);//生成rec 

	memset(vis,0,sizeof(vis));
	for (int j=size;j>=1;j--)
		if (!vis[rec[j]])	{
			num++;
			len[num]=q;
			dfs(rec[j]);
		}						//缩点完毕 
	//cout<<num<<endl;			
	len[num+1]=n;
	for (int i=1;i<=num;i++)
		for (int j=len[i]+1;j<=len[i+1];j++)
			ds[i]+=w[ans[j]];
	build();					//统计个缩点的权值和建图 
	for (int i=1;i<=num;i++)
		dp[i]=ds[i];
	memset(vis,0,sizeof(vis));
	for (int j=1;j<=num;j++)
		if (rd[j]==0)
			bfs(j);				//dp 
	int maxn=0;
	for (int i=1;i<=num;i++)
		maxn=max(dp[i],maxn);	
	cout<<maxn<<endl;
	return 0;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值