CF1872F Selling a Menagerie
思路
对于每一个动物,我们都尽量让它比它害怕的动物先被卖。
考虑拓扑排序,每次输出出度为 \(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("");//注意:答案是反着记录的。
}
}