[洛谷P3387]【模板】缩点

题目大意:给你一个有向图,让你找一条路径,使得该路径点权之和最大,如果重复经过只算一次。问最大是多少?

解题思路:对每个强连通分量,如果到了某一个点,必然要把这里的所有点走一遍,和才会最大(废话)。

于是我们缩点。

然后暴力、拓扑+DP貌似都行。

然后我Tarjan+暴力就过了(暴力出奇迹~(≧▽≦)/~)

C++ Code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<vector>
#define N 100005
#define reg register
using namespace std;
struct edge{
	int from,to,nxt;
}e[10*N<<1];
int head[N],n,m,low[N],dfn[N],cnt,stack[N],top,ltfl,ys[N],ans,f[N],a[N];
bool vis[N];
vector<int>v[N];
inline int readint(){
	char c=getchar();
	bool b=false;
	for(;!isdigit(c);c=getchar())b=c=='-';
	int d=0;
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^'0');
	return b?-d:d;
}
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
void tarjan(int now){
	dfn[now]=low[now]=++cnt;
	vis[stack[++top]=now]=true;
	for(reg int i=head[now];i;i=e[i].nxt)
	if(!dfn[e[i].to]){
		tarjan(e[i].to);
		low[now]=min(low[now],low[e[i].to]);
	}else
	if(vis[e[i].to])low[now]=min(low[now],dfn[e[i].to]);
	if(low[now]==dfn[now]){
		int v;
		f[++ltfl]=0;
		do{
			v=stack[top--];
			::v[ltfl].push_back(v);
			ys[v]=ltfl;
			vis[v]=false;
			f[ltfl]+=a[v];
		}while(v!=now);
	}
}
void dfs(int now,int s){
	vis[now]=true;
	for(reg int i=v[now].size()-1;i>=0;--i){
		for(reg int j=head[v[now][i]];j;j=e[j].nxt)
		if(!vis[ys[e[j].to]])dfs(ys[e[j].to],s+f[ys[e[j].to]]);
	}
	vis[now]=false;
	ans=max(ans,s);
}
int main(){
	ltfl=0;
	memset(head,0,sizeof head);
	n=readint(),m=readint();
	for(reg int i=1;i<=n;++i)a[i]=readint();
	for(reg int i=1;i<=m;++i){
		int u=readint(),v=readint();
		e[i]=(edge){u,v,head[u]};
		head[u]=i;
	}
	memset(vis,0,sizeof vis);
	top=0;
	memset(low,0,sizeof low);
	memset(dfn,0,sizeof dfn);
	for(reg int i=1;i<=n;++i)
	if(!dfn[i]){
		tarjan(i);
	}
	memset(vis,0,sizeof vis);
	ans=0;
	for(reg int i=1;i<=ltfl;++i)
	dfs(i,f[i]);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Mrsrz/p/7729499.html