#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200 * 1000 + 5;
vector<int> G[MAXN];
vector<int> seen, lowPoint, depth;
vector<pair<int, int>> st;
vector<int> c;
vector<int> path;
set<pair<int, int>> forbidden;
vector<int> inCycle;
bool foundSolution = false;
void dfsCycle(int node, int f, vector<int> &cycle) {
  seen[node] = 1;
  c.push_back(node);
  for (auto tmp : G[node]) {
    if (!cycle.empty()) return;
    if (tmp == f) continue;
    if (!seen[tmp]) {
      dfsCycle(tmp, node, cycle);
    } else {
      cycle.push_back(tmp);
      while (c.back() != tmp) {
        cycle.push_back(c.back());
        c.pop_back();
      }
    }
  }
  c.pop_back();
}
void dfsPath(int node, int f) {
  seen[node] = 1;
  c.push_back(node);
  for (auto tmp : G[node]) {
    if (!path.empty()) return;
    if (tmp == f || forbidden.find(make_pair(node, tmp)) != forbidden.end())
      continue;
    if (!seen[tmp]) {
      if (inCycle[tmp]) {
        path.push_back(tmp);
        while (!c.empty()) {
          path.push_back(c.back());
          c.pop_back();
        }
        return;
      } else {
        dfsPath(tmp, node);
      }
    }
  }
  c.pop_back();
}
bool solve(vector<pair<int, int>> component) {
  unordered_set<int> V;
  for (auto tmp : component) {
    V.insert(tmp.first);
    V.insert(tmp.second);
  }
  int n = V.size(), m = component.size();
  if (n < 3 || n == m) return false;
  for (auto tmp : V) n = max(tmp, n);
  ++n;
  for (int i = 0; i < n; ++i) G[i].clear();
  for (auto edge : component) {
    G[edge.first].push_back(edge.second);
    G[edge.second].push_back(edge.first);
  }
  vector<int> cycle;
  int root = *(V.begin());
  seen = vector<int>(n + 1, 0);
  dfsCycle(root, -1, cycle);
  int sz = cycle.size();
  for (int i = 0; i < sz; ++i) {
    forbidden.insert(make_pair(cycle[i], cycle[(i + 1) % sz]));
    forbidden.insert(make_pair(cycle[(i + 1) % sz], cycle[i]));
  }
  c.clear();
  inCycle = vector<int>(n + 1, 0);
  for (int i = 0; i < sz; ++i) inCycle[cycle[i]] = 1;
  seen = vector<int>(n + 1, 0);
  for (int i = 0; i < sz; ++i) {
    if (!path.empty()) continue;
    dfsPath(cycle[i], -1);
  }
  vector<vector<int>> sol(3, vector<int>());
  sol[0] = path;
  for (int i = 0; i < sz; ++i) {
    if (cycle[i] != path[0]) continue;
    for (int j = i; cycle[j] != path.back(); ++j) {
      sol[1].push_back(cycle[j]);
      if (j == sz - 1) j = -1;
    }
    sol[1].push_back(path.back());
    for (int j = i; cycle[j] != path.back(); --j) {
      sol[2].push_back(cycle[j]);
      if (j == 0) j = sz;
    }
    sol[2].push_back(path.back());
  }
  cout << "YES\n";
  for (int i = 0; i < 3; ++i) {
    cout << sol[i].size() << " ";
    for (auto tmp : sol[i]) cout << tmp + 1 << " ";
    cout << "\n";
  }
  exit(0);
  return true;
}
void dfs(int node, int f) {
  seen[node] = 1;
  lowPoint[node] = depth[node];
  for (auto tmp : G[node]) {
    if (tmp == f) continue;
    if (seen[tmp] && depth[tmp] >= depth[node]) continue;
    st.push_back(make_pair(node, tmp));
    if (!seen[tmp]) {
      depth[tmp] = depth[node] + 1;
      dfs(tmp, node);
      lowPoint[node] = min(lowPoint[node], lowPoint[tmp]);
      if (lowPoint[tmp] >= depth[node]) {
        bool erasedLast = false;
        vector<pair<int, int>> sol;
        while (!st.empty() && !erasedLast) {
          sol.push_back(st.back());
          if (st.back().first == node && st.back().second == tmp)
            erasedLast = true;
          st.pop_back();
        }
        foundSolution |= solve(sol);
      }
    } else {
      lowPoint[node] = min(lowPoint[node], depth[tmp]);
    }
  }
}
int main() {
  ios_base::sync_with_stdio(false);
  int n, m;
  cin >> n >> m;
  for (int i = 0; i < m; ++i) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    G[a].push_back(b);
    G[b].push_back(a);
  }
  lowPoint = vector<int>(n, 0);
  depth = vector<int>(n, 0);
  seen = vector<int>(n, 0);
  for (int i = 0; i < n; ++i)
    if (!foundSolution && !seen[i]) {
      dfs(i, -1);
    }
  cout << "NO\n";
}
