
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<float.h>
#include<time.h>
#include<limits.h>
#define forl(i,st,n) for(int i=st;i<n;i++)
#define form(i,end,n) for(int i=end;i<0;i--)
#define scan(c) scanf("%d",&c)
#define print(c) printf("%d",c)
#define scand(c) scanf("%lf",&c)
#define printd(c) printf("%lf",c)
#define newl printf("\n")
#define space printf(" ")
#define bar printf("-")


int max(int a,int b)
{
	if(a>b) return a;
	else return b;
}

int min(int a,int b)
{
	if(a<b) return a;
	else return b;
}

// void swap(int*a,int*b)
// {
// 	int c;
// 	c=*a;
// 	*a=*b;
// 	*b=c;
// }

// structure 'node' is a single node in a linked list
typedef struct A 
{
	int val;
	struct A* next;
} node;


// structure 'list' is the linked list implementation of adjacency list of a graph
typedef struct L
{
	node* st; 
	node* end;
	int sz; 
} list;



// push is used to push nodes in a linked list
// it first checks if the linked list is empty
// if it is it creates a new node and makes start and end pointers of struct list point to it
// else creates a node and makes the next pointer of the current end node point to it
void push(int v,int adj,list* ll)
{
	node* tp=(node*)malloc(sizeof(node));
	tp->val=adj;
	tp->next=NULL;
	if(ll[v].st==NULL)
	{
		ll[v].st=tp;
		ll[v].end=tp;
	}
	else
	{
		(ll[v].end)->next=tp;
		ll[v].end=tp;
	}
	ll[v].sz++;
}



// The following functions getp : get_parent and unite are functions for maintaining disjoint sets 
// getp works with path compressiong 
// unite works on the "unite by size" heuristic
int getp(int a,int*par)
{
	int ans;
	if(a == par[a]) ans=a;
	else
	{
		par[a] = getp(par[a],par);
		ans=par[a];
	} 
	return ans;
}


// we do a slight modification in unite function such that 
// at each time it also maintains the starting or source node of a 
// compressed component of immediate children
void unite(int a, int b,int* par,int* deg,int* src)
{
	a = getp(a,par);
	b = getp(b,par);
	if (a == b) return;

	src[b]=src[a];
	if (deg[a] < deg[b])
	{
		int c=a;
		a=b;
		b=c;
	}
	deg[a] += deg[b];
	par[b] = a;
}




// ts is a subroutine of topsort function
// it uses the 0,1,2 coloring of nodes to find cycles in directed graphs 
// and finds the reverse topological sort order
void ts(int v,int*flag,int *pos,int*used,int* ord,list *ll)
{
	used[v] = 1;
	node* tp=ll[v].st;
	while(tp)
	{
		int ch=tp->val;
		if(used[ch]==0)
		{
			ts(ch,flag,pos,used,ord,ll);
		}
		else if(used[ch]==1)
		{
			(*flag)=1;
		}
		if(*flag) return;
		tp=tp->next;
	}
	ord[*pos]=v;
	(*pos)+=1;
	used[v] = 2;
}


// topsort function calls ts and if there is no cycle returns true 
// with the topological sort order of the vertices
int topsort(int n,int*search,int*used,int*ord,list *ll)
{
	int pos=0;
 	int flag=0;
	forl(j,0,n)
	{
		int i=search[j];
		if(!used[i])
		{
			ts(i,&flag,&pos,used,ord,ll);
			if(flag) return 0;
		}
	}
	forl(i,0,n/2)
	{
		int c=ord[i];
		ord[i]=ord[n-1-i];
		ord[n-1-i]=c;
	}
	return 1;
}




// function dfs checks if the 'final schedule' found from the decompression of 
// topologically sorted compressed graph is valid or not

// it does so by checking if the position of a (immediate/non-immediate) child 
// in the 'final schedule' is before its parent or not 
// if yes the 'final schedule' is invalid 
// else return true
int dfs(int parent,int * pos,list *ll)
{
	node * tp=ll[parent].st;
	while(tp)
	{
		int child=tp->val;
		if (pos[child] < pos[parent]) return 0;
		if (!dfs(child,pos,ll)) return 0;
		tp=tp->next;
	}
	return 1;
}








int main()
{
	int n,m,i,j,k,t,a,b,c,d,e;
	int inf=INT_MAX;  // use DBL_MAX for floating point min and max 
	int mod=1e9+7;
	

	scan(n);
	scan(m);


	list * gp=(list*)malloc((n+5)*sizeof(list));
	// gp is the original graph
	list * im=(list*)malloc((n+5)*sizeof(list));
	// im is the graph of immediate children
	list * ng=(list*)malloc((n+5)*sizeof(list));
	// ng is the new compressed graph

	
	// Intializing all the graphs with start and end pointing to NULL and size 0
	forl(i,1,n+1)
	{
		gp[i].st=NULL;
		gp[i].end=NULL;
		gp[i].sz=0;

		ng[i].st=NULL;
		ng[i].end=NULL;
		ng[i].sz=0;

		im[i].st=NULL;
		im[i].end=NULL;
		im[i].sz=0;
	}

	int *search  =(int*)malloc((2*n+5)*sizeof(int));
	// search will store the nodes to be sent to topsort functions
	// for examples topsort function will only be called with nodes in the immediate graph for immediate graph's topsort
	// similiarly only representative elements have to be sent during topsort of compressed graph

	int *ct  =(int*)malloc((n+5)*sizeof(int));
	// int *ctx  =(int*)malloc((n+5)*sizeof(int));
	// // checks if all x'is are pairwise distinct by flagging if there are two same
	// // x'is in the input for immediate edges


	// int *cty  =(int*)malloc((n+5)*sizeof(int));
	// checks if all y'is are pairwise distinct by flagging if there are two same
	// y'is in the input for immediate edges

	int *ord  =(int*)malloc((n+5)*sizeof(int));		
	// ord will store the topsort order for each topsort

	int *fin  =(int*)malloc((n+5)*sizeof(int));
	// fin will store the final answer

	int *pos  =(int*)malloc((n+5)*sizeof(int));
	// pos stores the pos of nodes in the topsort ord
	// for example if we find ord to  be 	3 2 5 1 4
	// their corresponding positions are	1 2 3 5 4
	
	// hence, pos will be 4 2 1 5 3, since pos[3]=1,pos[2]=2, pos[5]=3 ....


	int *used =(int*)malloc((n+5)*sizeof(int));
	// used marks nodes which have been visited during the traversal of some other node during topsort

	int *vis  =(int*)malloc((n+5)*sizeof(int));
	// vis will store the visited nodes d


	// Below will all be used for the DSU data structure
	int *par =(int*)malloc((n+5)*sizeof(int));
	// par stores the parent of a disjoint set
	int *deg = (int*)malloc((n+5)*sizeof(int));
	// deg/degree stores the  size of an individual set
	int *src= (int*)malloc((n+5)*sizeof(int));
	// src stores the root node of each compressed component

	int root;
	int flag=0;
	int len=0;

	forl(i,1,n+1)
	{
		used[i]=0;
		vis[i]=0;

		// ctx[i]=0;
		// cty[i]=0;
		ct[i]=0;

		par[i]=i;
		deg[i]=1;
		src[i]=i;
	}





	// printf("Enter the parent node of the i'th node: ");
	forl(i,1,n+1)
	{
		scan(a);
		if(a)
		{
			push(a,i,gp);
  		}
		else
		{
			root=i;
		}
	}



	// printf("Enter immediate edges: ");newl;
	forl(i,0,m)
	{
		scan(a);scan(b);
		// if(ctx[a]) flag=1; 
		// else
		// {
		// 	ctx[a]=1;
		// 	search[len++]=a;
		// }
		// if(cty[b]) flag=1;
		// else
		// {
		// 	cty[b]=1;
		// 	search[len++]=b;
		// }
 				
		if(!ct[a])
		{
			search[len++]=a;
			ct[a]=1;
		}
		if(!ct[b])
		{
			ct[b]=1;
			search[len++]=b;
		}
		unite(a,b,par,deg,src);
		push(a,b,im);
	}





	// if(flag)
	// {
	// 	printf("All Xi's and Yi's must be pairwise distinct");
	// 	// print(0);
	// 	return 0;
	// }
	if(len && !topsort(len,search,used,ord,im))
	{
		// printf("Cycle detected in immediate graph");
		print(0);
		return 0;
	}

	len=0;
	forl(i,1,n+1)
	{
		node* tp=gp[i].st;
		if(getp(i,par)==i) 
		{
			search[len++]=i;
		}
		while(tp)
		{
			int v=i,u=tp->val;
			int vp=getp(v,par);
			int up=getp(u,par);
			if(vp!= up)
			{
				push(vp,up,ng);
			}
			tp=tp->next;
		}
	}
	forl(i,1,n+1) used[i]=0;
	if(!topsort(len,search,used,ord,ng))
	{
		// printf("Cycle detected in compressed graph");
		print(0);
		return 0;
	}









	int curr=0;
	forl(i,0,len)
	{
		int pr=getp(ord[i],par);
		pr=src[pr];
		if(!vis[pr])
		{
			vis[pr]=1;
			fin[curr++]=pr;
			pos[pr]=curr;

			node *tp;
			tp=im[pr].st;
			while(tp)
			{
				pr=tp->val;
				tp=im[pr].st;
				
				fin[curr++]=pr;
				pos[pr]=curr;
			}			
		}
	}

	if (!dfs(root,pos,gp))
	{
		// printf("Cycle detected between immediate and original graph");
		print(0);
		return 0;
	}

	forl(i,0,n)
	{
		print(fin[i]);space;
	}
	return 0;



}
