#include <bits/stdc++.h>
using namespace std;
int n, m;
bool napraw = false;
int x = -1, y = -1, nr_kraw;
vector<vector<int> > odp;
vector<int> spoj, izo;
struct kraw {
  int a, nr;
};
void Dodaj3(int a, int b, int c) {
  vector<int> t;
  t.push_back(a);
  t.push_back(b);
  t.push_back(c);
  odp.push_back(t);
}
void Dodaj1(int a) {
  vector<int> t;
  t.push_back(a);
  odp.push_back(t);
}
void Usun(vector<kraw>& a, int wie) {
  vector<kraw> t;
  int pos;
  for (int i = 0; i < a.size(); ++i)
    if (a[i].a == wie) pos = i;
  for (int i = 0; i < a.size(); ++i)
    if (i != pos) t.push_back(a[i]);
  a = t;
}
void Szukaj(vector<vector<kraw> >& G) {
  vector<bool> odw(n, false);
  stack<int> S;
  for (int i = 0; i < n; ++i) {
    if (!odw[i]) {
      vector<int> spo;
      set<int> K;
      int il_kraw = 0;
      odw[i] = true;
      S.push(i);
      while (!S.empty()) {
        int akt = S.top();
        S.pop();
        spo.push_back(akt);
        il_kraw += G[akt].size();
        for (int j = 0; j < G[akt].size(); ++j) {
          if (!odw[G[akt][j].a]) {
            K.insert(G[akt][j].nr);
            odw[G[akt][j].a] = true;
            S.push(G[akt][j].a);
          }
        }
      }
      il_kraw /= 2;
      if (il_kraw >= 2) {
        if (il_kraw == spo.size() - 1) {
          if (x == -1) {
            for (int j = 0; j < spo.size(); ++j) {
              if (G[spo[j]].size() >= 2) {
                x = spo[j];
                y = G[spo[j]][0].a;
                nr_kraw = G[spo[j]][0].nr;
              }
            }
          }
        } else {
          for (int j = 0; j < spo.size(); ++j) {
            for (int k = 0; k < G[spo[j]].size(); ++k) {
              if (K.find(G[spo[j]][k].nr) == K.end()) {
                x = spo[j];
                y = G[spo[j]][k].a;
                nr_kraw = G[spo[j]][k].nr;
                return;
              }
            }
          }
        }
      }
    }
  }
}
void Napraw(vector<vector<kraw> >& G) {
  Usun(G[x], y);
  Usun(G[y], x);
  G[x].push_back({0, -1});
  G[0].push_back({x, -1});
  Dodaj3(nr_kraw, 0, x);
}
void Podziel(vector<vector<kraw> >& G) {
  vector<bool> odw(n, false);
  stack<int> S;
  for (int i = 0; i < n; ++i) {
    if (!odw[i]) {
      odw[i] = true;
      S.push(i);
      int wlk = 0;
      while (!S.empty()) {
        int akt = S.top();
        S.pop();
        ++wlk;
        for (int j = 0; j < G[akt].size(); ++j) {
          if (!odw[G[akt][j].a]) {
            odw[G[akt][j].a] = true;
            S.push(G[akt][j].a);
          }
        }
      }
      if (wlk == 1)
        izo.push_back(i);
      else
        spoj.push_back(i);
    }
  }
}
void dfs(int akt, int oj, vector<vector<kraw> >& G, vector<bool>& odw,
         vector<bool>& jest_niedrz, int nr) {
  odw[akt] = true;
  Dodaj1(akt);
  vector<bool> drz(G[akt].size(), false);
  for (int i = 0; i < G[akt].size(); ++i) {
    if (!odw[G[akt][i].a]) {
      dfs(G[akt][i].a, akt, G, odw, jest_niedrz, nr);
      if (!(akt == 0 && napraw)) Dodaj1(akt);
      drz[i] = true;
    }
  }
  if (akt == 0 && napraw) return;
  for (int i = 0; i < G[akt].size(); ++i) {
    if (!drz[i] && G[akt][i].a != oj && jest_niedrz[G[akt][i].nr]) {
      if (izo.size() != 0) {
        Dodaj3(G[akt][i].nr, akt, izo.back());
        Dodaj1(izo.back());
        Dodaj1(akt);
        izo.pop_back();
        jest_niedrz[G[akt][i].nr] = false;
      }
    }
  }
  int nr_nast = -2;
  if (nr < spoj.size() - 1) {
    if (nr != 0) {
      if (akt == spoj[nr]) {
        nr_nast = G[akt].size() - 1;
        while (!drz[nr_nast]) --nr_nast;
        nr_nast = G[akt][nr_nast].nr;
      }
    } else {
      if (napraw) {
        if (akt == x) {
          nr_nast = G[akt].size() - 1;
          while (!drz[nr_nast]) --nr_nast;
          nr_nast = G[akt][nr_nast].nr;
        }
      } else {
        if (akt == spoj[nr]) {
          nr_nast = G[akt].size() - 1;
          while (!drz[nr_nast]) --nr_nast;
          nr_nast = G[akt][nr_nast].nr;
        }
      }
    }
  }
  for (int i = 0; i < G[akt].size(); ++i) {
    if (drz[i]) {
      if (G[akt][i].nr != nr_nast) {
        if (izo.size() != 0) {
          Dodaj3(G[akt][i].nr, akt, izo.back());
          Dodaj1(izo.back());
          Dodaj1(akt);
          izo.pop_back();
        }
      } else
        Dodaj3(G[akt][i].nr, akt, spoj[nr + 1]);
    }
  }
}
int main() {
  ios_base::sync_with_stdio(0);
  cin >> n >> m;
  if (n == 1 && m == 0) {
    cout << "YES" << endl;
    cout << "0" << endl << "1 1";
    return 0;
  }
  vector<vector<kraw> > G(n);
  for (int i = 0; i < m; ++i) {
    int a, b;
    cin >> a >> b;
    --a;
    --b;
    G[a].push_back({b, i});
    G[b].push_back({a, i});
  }
  if (G[0].size() == 0) {
    napraw = true;
    Szukaj(G);
    if (x == -1) {
      cout << "NO";
      return 0;
    }
    Napraw(G);
  }
  Podziel(G);
  vector<bool> odw(n, false);
  vector<bool> jest_niedrz(m, true);
  for (int i = 0; i < spoj.size(); ++i)
    dfs(spoj[i], -1, G, odw, jest_niedrz, i);
  if (izo.size() != 0) {
    cout << "NO";
    return 0;
  }
  cout << "YES" << endl;
  vector<vector<int> > odp2;
  if (odp[0].size() == 3) swap(odp[0], odp[1]);
  int il_zam = 0;
  for (int l = 0; l < odp.size();) {
    if (odp[l].size() == 3) {
      ++il_zam;
      odp2.push_back(odp[l]);
      ++l;
      continue;
    }
    int p = l;
    while (p < odp.size() && odp[p].size() == 1) ++p;
    --p;
    vector<int> t;
    for (int i = l; i <= p; ++i) t.push_back(odp[i][0]);
    odp2.push_back(t);
    l = p + 1;
  }
  cout << il_zam << endl;
  for (int i = 0; i < odp2.size(); ++i) {
    if (i % 2 == 0) {
      cout << odp2[i].size() << " ";
      for (int j = 0; j < odp2[i].size(); ++j) cout << odp2[i][j] + 1 << " ";
    } else
      cout << odp2[i][0] + 1 << " " << odp2[i][1] + 1 << " " << odp2[i][2] + 1;
    cout << endl;
  }
  return 0;
}
