#include <bits/stdc++.h>
using namespace std;
/// OBS: Fiecare comp biconexa are cel mult 15 noduri !!!
const int NR_MAX = 18, ST_MAX = 1 << NR_MAX;
const int INF = 1e9 + 5;
vector<bool> visited;
vector<vector<int> > G;
vector<int> level, low;
stack<int> Stack;
vector<bool> isCriticalNode;
vector<pair<int, int> > criticalEdges;
vector<vector<int> > biconex;
vector<int> comp;
void SetInput(string name)
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
(void)!freopen((name + ".in").c_str(), "r", stdin);
(void)!freopen((name + ".out").c_str(), "w", stdout);
}
void FinishProgram()
{
cout << "No chance\n";
exit(0);
}
void DFS_CB(int v, int t, int root) /// Pentru a calcula comp biconexe
{
visited[v] = true;
low[v] = level[v] = level[t] + 1;
Stack.push(v);
int nrChildren = 0;
for(const int u : G[v])
if(u != t)
{
if(visited[u])
{
low[v] = min(low[v], level[u]);
continue;
}
DFS_CB(u, v, root);
low[v] = min(low[v], low[u]);
nrChildren++;
if(v != root && level[v] <= low[u])
isCriticalNode[v] = true;
if(level[v] < low[u])
criticalEdges.emplace_back(v, u);
if(level[v] <= low[u])
{
biconex.push_back(vector<int>(0));
int x;
do{
x = Stack.top();
Stack.pop();
biconex[biconex.size() - 1].push_back(x);
comp[x] = biconex.size() - 1;
} while(x != u);
biconex[biconex.size() - 1].push_back(v);
comp[v] = biconex.size() - 1;
}
}
if(v == root && nrChildren >= 2)
isCriticalNode[root] = true;
}
vector<int> brutePath;
bool DFS(int v, int E) /// Pentru a calcula prin ce comp biconexe trece drumul de la S la E
{
visited[v] = true;
brutePath.push_back(v);
if(v == E)
return true;
for(const int u : G[v])
if(not visited[u])
if(DFS(u, E))
return true;
brutePath.pop_back();
return false;
}
vector<int> path;
struct HamiltonNode
{
bool possible;
int lastNode;
HamiltonNode() { }
HamiltonNode(bool _possible, int _lastNode) :
possible(_possible), lastNode(_lastNode) { }
};
HamiltonNode dp[ST_MAX][NR_MAX];
void AddHamiltonianPath(int mask, int crr, int startNode)
{
if(crr == startNode)
return;
AddHamiltonianPath((mask ^ (1 << crr)), dp[mask][crr].lastNode, startNode);
path.push_back(crr);
}
void FindPathInBiconexComp(int compId, int startNode, int endNode) /// punem in drum nodurile din intervalul [start, end] din drumul hamiltonian
/// dar daca endNode == -1, pundem din intervalul [start, x] oricare ar fi x posibil
{
int nodesCnt = biconex[compId].size();
assert(find(biconex[compId].begin(), biconex[compId].end(), startNode) != biconex[compId].end());
if(endNode != -1) assert(find(biconex[compId].begin(), biconex[compId].end(), endNode) != biconex[compId].end());
vector<vector<int>> G2(nodesCnt, vector<int>());
unordered_map<int, int> newNodeId, oldNodeId;
for(int i = 0; i < (int)biconex[compId].size(); i++)
{
int v = biconex[compId][i];
newNodeId[v] = i;
oldNodeId[i] = v;
}
for(auto [newId, oldId] : oldNodeId)
for(const int& u : G[oldId])
if(newNodeId.count(u) != 0)
G2[newId].push_back(newNodeId[u]);
assert(newNodeId.count(startNode) != 0);
int newStartNode = newNodeId[startNode];
int maxMask = (1 << nodesCnt) - 1;
for(int mask = 0; mask <= maxMask; mask++)
for(int v = 0; v < nodesCnt; v++)
dp[mask][v].possible = false;
dp[(1 << newStartNode)][newStartNode] = HamiltonNode(true, -1);
for(int mask = 0; mask < maxMask; mask++)
for(int v = 0; v < nodesCnt; v++)
if(dp[mask][v].possible)
for(const int& u : G2[v])
if((mask & (1 << u)) == 0)
dp[(mask ^ (1 << u))][u] = HamiltonNode(true, v);
bool found = false;
if(endNode == -1) /// Putem alege orice nod la sfarsit
{
for(int v = 0; v < nodesCnt && not found; v++)
if(dp[maxMask][v].possible)
{
found = true;
AddHamiltonianPath(maxMask, v, startNode);
for(int i = (int)path.size() - nodesCnt + 1; i < (int)path.size(); i++) /// Schimbam nodurile in id-ul lor din graful normal
path[i] = oldNodeId[path[i]];
}
}
else
{
int newEndNode = newNodeId[endNode];
if(dp[maxMask][newEndNode].possible)
{
found = true;
AddHamiltonianPath((maxMask ^ (1 << newEndNode)), dp[maxMask][newEndNode].lastNode, startNode); /// Schimbam nodurile in id-ul lor din graful normal
for(int i = (int)path.size() - nodesCnt + 2; i < (int)path.size(); i++)
path[i] = oldNodeId[path[i]];
}
}
if(not found)
{
FinishProgram();
}
}
void Solve()
{
int N, M;
int S, E, Q;
cin >> N >> M;
G.resize(N + 1);
visited.resize(N + 1, false);
isCriticalNode.resize(N + 1, false);
level.resize(N + 1, INF);
low.resize(N + 1, INF);
comp.resize(N + 1, -1);
for(int i = 1, u , v; i <= M; i++)
{
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
cin >> S >> E >> Q;
DFS_CB(S, 0, S);
if(not visited[E] || not visited[Q]) /// Nu exista un drum de la S la E sau Q
FinishProgram();
if(isCriticalNode[S] && isCriticalNode[E])
FinishProgram();
if(isCriticalNode[S]) /// S este nod critic
{
swap(E, S);
if(find(biconex[comp[S]].begin(), biconex[comp[S]].end(), Q) == biconex[comp[S]].end())
FinishProgram();
}
else if(find(biconex[comp[S]].begin(), biconex[comp[S]].end(), Q) == biconex[comp[S]].end()) /// S si Q NU sunt in aceeasi comp biconexa
{
swap(E, S);
if(find(biconex[comp[S]].begin(), biconex[comp[S]].end(), Q) == biconex[comp[S]].end())
FinishProgram();
}
visited.assign(N + 1, false);
DFS(Q, E); /// DFS ca sa gasim prin ce comp biconexe trece drumul de la Q sa E
isCriticalNode[Q] = true; /// Consideram Q critic ca sa incepem din el in for pe brutepath
if(isCriticalNode[brutePath[brutePath.size() - 1]] && not isCriticalNode[brutePath[brutePath.size() - 2]])
brutePath.pop_back(); /// Stim ca ultimul nod din brutePath este E, dar nu trebuie neaparat sa ne oprim in el, ci in orice nod din comb lui boconexa
brutePath.erase(unique(brutePath.begin(), brutePath.end(), [](int a, int b){ /// vrem ca fiecare nod din brutePath sa fie ori un nod critic, ori un nod reprezentant unei comp biconexe
return not isCriticalNode[a] && not isCriticalNode[b] && comp[a] == comp[b];
}), brutePath.end());
return;
int len = brutePath.size();
for(int i = 0; i < len; i++)
if(isCriticalNode[brutePath[i]])
path.push_back(brutePath[i]);
else if(i == len - 1)
FindPathInBiconexComp(comp[brutePath[i]], brutePath[i-1], -1);
else
FindPathInBiconexComp(comp[brutePath[i]], brutePath[i-1], brutePath[i+1]);
path.erase(unique(path.begin(), path.end()), path.end());
cout << path.size() << '\n';
for(const int& v : path)
cout << v << ' ';
cout << '\n';
}
int main()
{
SetInput("santa");
Solve();
return 0;
}