CF1872F Selling a Menagerie

发布时间 2023-09-08 08:27:47作者: One_JuRuo

思路

对于每一个动物,我们都尽量让它比它害怕的动物先被卖。

考虑拓扑排序,每次输出出度为 \(0\) 的点,然后再删点删边。

但是 \(n\) 个点,\(n\) 条边,必然存在环,所以只用拓扑排序是不行的。

自然想到 tarjan 缩点,对于环外,就拓扑排序好了,对于一个环,显然无法满足所有的点,所以我们就直接选择值最小的点放最后就好了。

原本我是 tarjan 再去拓扑的,但是调得我脑阔疼,又想到 tarjan 缩点的顺序本来就是反的拓扑序,所以可以边 tarjan 边记录答案,反着记就好。

另外提一句,tarjan 真的难调,一生之敌啊啊啊。

AC code

#include<bits/stdc++.h>
using namespace std;
int T,n,a,v[100005],cdfn,dfn[100005],low[100005],z[100005],top,in_z[100005],minn,p;
int e[200005],ne[200005],h[200005],idx,ans[100005],ccnt;
int tem[200005],tcnt;
inline void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void dfs(int u)
{
	dfn[u]=low[u]=++cdfn,z[++top]=u,in_z[u]=1;
	for(int i=h[u];i;i=ne[i])
		if(!dfn[e[i]]) dfs(e[i]),low[u]=min(low[u],low[e[i]]);
		else if(in_z[e[i]]) low[u]=min(low[u],dfn[e[i]]);
	if(dfn[u]==low[u])
	{
		tcnt=1,p=1,minn=v[z[top]],tem[1]=z[top],in_z[z[top--]]=0;
		while(z[top+1]!=u)//栈中顺序就是一种遍历顺序
		{
			tem[++tcnt]=z[top];
			if(minn==-1) minn=v[z[top]],p=tcnt;
			else if(minn>v[z[top]]) minn=v[z[top]],p=tcnt;//记录最小值的位置
			in_z[z[top--]]=0;
		}
		for(int i=p;i<=tcnt;++i) ans[++ccnt]=tem[i];
		for(int i=1;i<p;++i) ans[++ccnt]=tem[i];//记录答案
	}
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		ccnt=0,cdfn=0,memset(dfn,0,sizeof(dfn)),memset(low,0,sizeof(low)),memset(h,0,sizeof(h)),idx=1,memset(in_z,0,sizeof(in_z)),top=0;
		scanf("%d",&n);
		for(int i=1;i<=n;++i) scanf("%d",&a),add(i,a);//建边
        for(int i=1;i<=n;++i) scanf("%d",&v[i]);
		for(int i=1;i<=n;++i) if(!dfn[i]) dfs(i);//tarjan
		for(int i=ccnt;i>=1;--i) printf("%d ",ans[i]);puts("");//注意:答案是反着记录的。
	}
}