#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;
int S, E, Q;
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;
}
bool foundE = false;
vector<int> compBiconexInPath, criticalNodesInPath;
void DFS(int v) /// Pentru a calcula prin ce comp biconexe trece drumul de la S la E
{
visited[v] = true;
if(v == E)
{
foundE = true;
return;
}
for(const int u : G[v])
if(not visited[u] && not foundE)
{
if(not foundE && comp[v] != comp[u])
{
compBiconexInPath.push_back(comp[u]);
criticalNodesInPath.push_back(u);
}
DFS(u);
if(not foundE && comp[v] != comp[u])
{
compBiconexInPath.pop_back();
criticalNodesInPath.pop_back();
}
}
}
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)
{
if(crr == -1)
return;
AddHamiltonianPath((mask ^ (1 << crr)), dp[mask][crr].lastNode);
path.push_back(crr);
}
void FindPathInBiconexComp(int compId, int startNode, int endNode)
{
int nodesCnt = biconex[compId].size();
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 && (endNode == -1 || newNodeId.count(endNode) != 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);
}
}
else
{
int newEndNode = newNodeId[endNode];
if(dp[maxMask][newEndNode].possible)
{
found = true;
AddHamiltonianPath(maxMask, newEndNode);
}
}
if(found) /// Schimbam nodurile in id-ul lor din graful normal
{
assert((int)path.size() >= nodesCnt);
for(int i = (int)path.size() - nodesCnt; i < (int)path.size(); i++)
path[i] = oldNodeId[path[i]];
}
else /// Nu putem indeplini cerinta
FinishProgram();
}
void Solve()
{
int N, M;
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;
assert(S != Q && E != Q);
DFS_CB(S, 0, S);
if(not visited[E]) /// Nu exista un drum de la S la E
FinishProgram();
if(isCriticalNode[S] || isCriticalNode[Q]) /// S sau Q sunt noduri critice
FinishProgram();
if(find(biconex[comp[S]].begin(), biconex[comp[S]].end(), Q) == biconex[comp[S]].end()) /// S si Q NU sunt in aceeasi comp biconexa
FinishProgram();
compBiconexInPath.push_back(comp[Q]);
criticalNodesInPath.push_back(Q); /// punem Q ca nod critic in drum deoarece din el incepem drumul de la Q la E
visited.assign(N + 1, false);
DFS(Q); /// DFS ca sa gasim prin ce comp biconexe trece drumul de la Q sa E
for(int i = 0; i < (int)compBiconexInPath.size(); i++)
if(i == (int)compBiconexInPath.size() - 1)
FindPathInBiconexComp(compBiconexInPath[i], criticalNodesInPath[i], -1);
else
FindPathInBiconexComp(compBiconexInPath[i], criticalNodesInPath[i], criticalNodesInPath[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;
}