P3387 【模板】缩点

Aimee

tarjan是一个好东西

如果这个题没有环,那很简单

讨厌环

那就缩点,毕竟说了一个边只算一次

tarjan找到强联通分量,然后缩成一个点

怎么做呢?

有一个dfn,也就是时间戳,还有一个low,表示能向下到达的最小的时间戳

然后dfssssssssssssssssssssssssssssssssssssssssssssssss

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
using namespace std;
struct b{
	int to;
	int ne;
}e[100005][2];
int n,m;
stack <int> s;
int p;
int Aimee[10005];
int head[10005][2];
void add(int f,int to,int con){
	p++;
	e[p][con].ne=head[f][con];
	e[p][con].to=to;
	head[f][con]=p;
	return ;
}
int x,y;
int tim;
int num[10005];
int dfn[10005],low[10005];
int sim[10005];
int Archie;
void dfs(int now){
	tim++;
	dfn[now]=low[now]=tim;
	s.push(now);
	for(int i=head[now][0];i;i=e[i][0].ne){
		int v=e[i][0].to;
		if(!dfn[v]){//没遍历过 
			dfs(v);
			low[now]=min(low[now],low[v]);
		}else if(num[v]==0){//还没有被标记,虽然被遍历过了 
			low[now]=min(low[now],dfn[v]);
		}
	}
	if(dfn[now]==low[now]){//这就是一个点啊 
		Archie++;
		while(1){
			x=s.top();
			s.pop();
			sim[Archie]+=Aimee[x];
			num[x]=Archie;
			if(x==now)break;
		}
	}
}
int dp[10005];
void dfss(int now){
	if(dp[now]) return ;
	dp[now]=max(dp[now],sim[now]);
	for(int i=head[now][1];i;i=e[i][1].ne){
		int v=e[i][1].to;
		dfss(v);
		dp[now]=max(dp[now],dp[v]+sim[now]);
	}
}
int forever;
int main(){
	scanf("%d%d",&n,&m); 
	for(int i=1;i<=n;++i){
		scanf("%d",&Aimee[i]);
	} 
	for(int i=1;i<=m;++i){
		scanf("%d%d",&x,&y);
		add(x,y,0); 
	}
	for(int i=1;i<=n;++i){
		if(!dfn[i])
		dfs(i);
	}
	p=0;
	for(int i=1;i<=n;++i){
		for(int j=head[i][0];j;j=e[j][0].ne){
			x=e[j][0].to;
			if(num[x]!=num[i]){
				add(num[i],num[x],1);
			}
		}
	}
	for(int i=1;i<=Archie;++i){
		dfss(i);
	}
	for(int i=1;i<=Archie;++i){
		forever=max(forever,dp[i]);
	}
	cout<<forever;
	return 0;
}
posted @ 2020-11-04 21:49  Simex  阅读(73)  评论(0编辑  收藏  举报