P3916 题解

146 阅读1分钟

题目链接

思路1

对于一个DAG,可以通过记忆化搜索得到答案。从每一个没有被访问过的点(即ans值为0)开始搜索,不断更新ans数组即可,如果搜到一个ans值不为0的点,那么直接用其ans值更新当前点的ans,不需要再搜一遍。
但是如果出现环,按照原先的搜索方式没法找到一个环中编号最大的点。因为在一个环中当第二次搜索到起点时,递归还没有返回过起点,所以起点的答案仍然没有被更新,也就是说此时这个环内编号最大的点并没有被维护到起点的ans中去。考虑通过缩点将原图转化为DAG,再按照DAG的思路求解。
据说对原图每个点直接dfs100遍也能过。

代码

#include <bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
using namespace std;
const int N=1E5+10,M=1E5+10;
int n,m,tot,timer,cnt;
int head[N],dfsn[N],fa[N],insta[N],ans[N],low[N],a[N];
stack<int> s;
struct Edge
{
	int frm,to,nxt;
}e[M];
void add_edge(int u,int v)
{
	e[++tot].nxt=head[u];e[tot].frm=u;e[tot].to=v;head[u]=tot;
}
inline int read()
{
	int ret=0;char ch=getchar();
	while(ch<'0' || ch>'9')
		ch=getchar();
	while(ch>='0' && ch<='9')
	{
		ret=ret*10+ch-'0';
		ch=getchar();
	}
	return ret;
}
void tarjan(int u)
{
	dfsn[u]=low[u]=++timer;
	s.push(u);
	insta[u]=1;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(!dfsn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(insta[v] && dfsn[v]<low[u])
				low[u]=dfsn[v];
	}
	if(low[u]==dfsn[u])
	{
		++cnt;
		int cur;
		do
		{
			cur=s.top();
			s.pop();
			insta[cur]=0;
			fa[cur]=cnt;
			a[cnt]=max(a[cnt],cur);
		}
		while(cur!=u);
	}
}
void dfs(int u)
{
	if(ans[u])
		return;
	ans[u]=a[u];
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		dfs(v);
		ans[u]=max(ans[u],ans[v]);
	}
}
int main()
{
//	freopen("in.in","r",stdin);
	n=read();m=read();
	rep(i,1,m)
	{
		int u,v;
		u=read();v=read();
		add_edge(u,v);
	}
	rep(i,1,n)
	{
		if(!dfsn[i])
			tarjan(i);
	}
	tot=0;
	memset(head,0,sizeof(head));
	rep(i,1,m)
	{
		int u=fa[e[i].frm],v=fa[e[i].to];
		if(u==v)
			continue;
		add_edge(u,v);
	}
	rep(i,1,cnt)
	{
		if(!ans[i])
			dfs(i);
	}
	rep(i,1,n)
		printf("%d ",ans[fa[i]]);
	printf("\n");
}

思路2

正难则反,反着做超级简单。考虑大的点反向能到达哪些点,反向建图,按照点编号从大到小dfs,就能保证每个点都能取到最优解。

代码

#include <bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
using namespace std;
const int N=1E5+10,M=N;
int n,m,tot;
int ans[N],head[N];
struct Edge
{
	int nxt,to;
}e[M];
inline int read()
{
	int ret=0;char ch=getchar();
	while(ch<'0' || ch>'9')
		ch=getchar();
	while(ch>='0' && ch<='9')
	{
		ret=ret*10+ch-'0';
		ch=getchar();
	}
	return ret;
}
inline void add_edge(int u,int v)
{
	e[++tot].nxt=head[u];e[tot].to=v;head[u]=tot;
}
void dfs(int u,int fa)
{
	if(ans[u])
		return;
	ans[u]=fa;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		dfs(v,fa);
	}
}
int main()
{
	n=read();m=read();
	rep(i,1,m)
	{
		int u,v;
		u=read();v=read();
		add_edge(v,u);
	}
	for(int i=n;i>0;--i)
	{
		dfs(i,i);
	}
	rep(i,1,n)
		printf("%d ",ans[i]);
	cout<<endl;
}