using System;
using System.Collections.Generic;
using System.Linq;
using CompLib.Algorithm;
using CompLib.Graph;
using CompLib.Util;

public class Program
{
    private int N;
    private int M;
    private int K;

    private TreeEx T;

    private List<E> UnUsed;

    public void Solve()
    {
        var sc = new Scanner();
        N = sc.NextInt();
        M = sc.NextInt();
        K = sc.NextInt();

        var uf = new UnionFind(N);
        var ls = new List<int>[N];
        for (int i = 0; i < N; i++)
        {
            ls[i] = new List<int>();
        }

        UnUsed = new List<E>();

        for (int i = 0; i < M; i++)
        {
            int u = sc.NextInt() - 1;
            int v = sc.NextInt() - 1;
            if (uf.Unite(u, v))
            {
                ls[u].Add(v);
                ls[v].Add(u);
            }
            else
            {
                UnUsed.Add(new E(u, v));
            }
        }

        T = new TreeEx(ls);

        if (M == N - 1)
        {
            // 木
            Tree();
        }
        else
        {
            NonTree();
        }
    }

    void Tree()
    {
        // 二分グラフ
        List<int> b = new List<int>();
        List<int> w = new List<int>();
        for (int i = 0; i < N; i++)
        {
            if (T.Depth(i) % 2 == 0) b.Add(i);
            else w.Add(i);
        }

        int t = (K + 1) / 2;

        var l = b.Count > w.Count ? b : w;
        var ans = new List<int>();
        for (int i = 0; i < t; i++)
        {
            ans.Add(l[i] + 1);
        }

        Console.WriteLine(1);
        Console.WriteLine(string.Join(" ", ans));
    }

    void NonTree()
    {
        // 小さいサークル
        foreach (E e in UnUsed)
        {
            var a = T.LowestCommonAncestor(e.S, e.T);
            int dist = T.Depth(e.S) + T.Depth(e.T) - 2 * T.Depth(a);
            if (dist + 1 <= K)
            {
                Console.WriteLine(2);
                var ls = Circle(e.S, a, e.T);
                Console.WriteLine(ls.Count);
                Console.WriteLine(string.Join(" ", ls));
                return;
            }
        }

        // K以下のループが無い

        var aa = T.LowestCommonAncestor(UnUsed[0].S, UnUsed[0].T);
        var c = Circle(UnUsed[0].S, aa, UnUsed[0].T);
        // cを一つおきに
        var t = (K + 1) / 2;
        var ans = new List<int>();
        for (int i = 0; i < t; i++)
        {
            ans.Add(c[i * 2]);
        }

        Console.WriteLine(1);
        Console.WriteLine(string.Join(" ", ans));
    }

    List<int> Circle(int s, int a, int t)
    {
        var sToa = new List<int>();
        var tToa = new List<int>();
        int cur = s;
        while (cur != a)
        {
            sToa.Add(cur + 1);
            cur = T.Parent(cur);
        }

        cur = t;
        while (cur != a)
        {
            tToa.Add(cur + 1);
            cur = T.Parent(cur);
        }

        sToa.Add(a + 1);
        for (int i = tToa.Count() - 1; i >= 0; i--)
        {
            sToa.Add(tToa[i]);
        }

        return sToa;
    }

    public static void Main(string[] args) => new Program().Solve();
}

struct E
{
    public int S, T;

    public E(int s, int t)
    {
        S = s;
        T = t;
    }
}

namespace CompLib.Graph
{
    class UnionFind
    {
        private readonly int[] _parent, _size;

        public UnionFind(int size)
        {
            _parent = new int[size];
            _size = new int[size];
            for (int i = 0; i < size; i++)
            {
                _parent[i] = i;
                _size[i] = 1;
            }
        }

        /// <summary>
        /// iが含まれる木の根を調べる
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public int Find(int i) => _parent[i] == i ? i : Find(_parent[i]);

        /// <summary>
        /// x,yが同じグループに含まれるか?
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public bool Same(int x, int y) => Find(x) == Find(y);

        /// <summary>
        /// xとyを同じグループにする 元々同じグループならfalseを返す
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public bool Unite(int x, int y)
        {
            x = Find(x);
            y = Find(y);
            if (x == y) return false;

            // データ構造をマージする一般的なテク
            if (_size[x] > _size[y])
            {
                _parent[y] = x;
                _size[x] += _size[y];
            }
            else
            {
                _parent[x] = y;
                _size[y] += _size[x];
            }

            return true;
        }

        /// <summary>
        /// iが含まれるグループのサイズ
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public int GetSize(int i) => _size[Find(i)];
    }
}

namespace CompLib.Algorithm
{
    public class Tree
    {
        private readonly List<int>[] _edge;
        protected List<int>[] _child;
        protected readonly int _root;
        protected readonly int _size;

        private readonly int[] _depth;
        private readonly int[] _parent;

        public Tree(List<int>[] edge, int root = 0)
        {
            _root = root;
            _size = edge.Length;
            _edge = edge;
            _child = new List<int>[_size];
            for (int i = 0; i < _size; i++)
            {
                _child[i] = new List<int>();
            }

            _depth = new int[_size].Select(i => -1).ToArray();
            _parent = new int[_size];

            _depth[_root] = 0;
            _parent[_root] = -1;
            var q = new Queue<int>();
            q.Enqueue(_root);

            while (q.Count > 0)
            {
                var d = q.Dequeue();
                foreach (int i in _edge[d])
                {
                    if (_depth[i] != -1) continue;
                    _child[d].Add(i);
                    _depth[i] = _depth[d] + 1;
                    _parent[i] = d;
                    q.Enqueue(i);
                }
            }
        }

        /// <summary>
        /// 各頂点の深さ
        /// </summary>
        /// <returns></returns>
        public int[] DepthAll()
        {
            return _depth;
        }

        /// <summary>
        /// 頂点nの深さ
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public int Depth(int n)
        {
            return _depth[n];
        }

        /// <summary>
        /// 各頂点の親 根の親は-1
        /// </summary>
        /// <returns></returns>
        public int[] ParentAll()
        {
            return _parent;
        }

        /// <summary>
        /// 頂点nの親 nが根なら-1を返す
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public int Parent(int n)
        {
            return _parent[n];
        }

        /// <summary>
        /// 根がnの部分木の頂点集合
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public int[] SubTreeNodes(int n)
        {
            var l = new List<int>();
            SearchNodes(n, l);
            return l.ToArray();
        }

        private void SearchNodes(int r, List<int> l)
        {
            l.Add(r);
            foreach (int i in _child[r])
            {
                SearchNodes(i, l);
            }
        }

        public int DeepestSubtreeNode(int r, out int node)
        {
            var l = SubTreeNodes(r);

            int result = Depth(r);
            node = r;

            foreach (int i in l)
            {
                if (Depth(i) > result)
                {
                    result = Depth(i);
                    node = i;
                }
            }

            return result;
        }
    }
}

namespace CompLib.Algorithm
{
    /// <summary>
    /// Treeに機能追加
    /// </summary>
    public class TreeEx : Tree
    {
        private readonly List<int>[] _ancestor;

        public TreeEx(List<int>[] tree, int root = 0) : base(tree, root)
        {
            _ancestor = new List<int>[_size];
            for (int i = 0; i < _size; i++)
            {
                _ancestor[i] = new List<int>();
            }

            var q = new Queue<int>();
            q.Enqueue(_root);
            while (q.Count > 0)
            {
                var d = q.Dequeue();
                if (Depth(d) > 0)
                {
                    _ancestor[d].Add(Parent(d));
                    for (int i = 0; i < _ancestor[_ancestor[d][i]].Count; i++)
                    {
                        _ancestor[d].Add(_ancestor[_ancestor[d][i]][i]);
                    }
                }

                foreach (int i in _child[d])
                {
                    q.Enqueue(i);
                }
            }
        }

        /// <summary>
        /// 頂点nのi世代上
        /// </summary>
        /// <param name="n"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        public int Ancestor(int n, int i)
        {
            if (i == 0)
            {
                return n;
            }

            for (int t = 0;; t++)
            {
                if (((1 << t) & i) > 0)
                {
                    return Ancestor(_ancestor[n][t], i - (1 << t));
                }
            }
        }

        public int LowestCommonAncestor(int a, int b)
        {
            if (Depth(b) < Depth(a))
            {
                return LowestCommonAncestor(b, a);
            }

            // depth(a) <= depth(b)
            b = Ancestor(b, Depth(b) - Depth(a));
            if (a == b) return a;

            int ok = -1;
            int ng = _ancestor[a].Count;
            while (ng - ok > 1)
            {
                int med = (ok + ng) / 2;
                if (_ancestor[a][med] == _ancestor[b][med])
                {
                    ng = med;
                }
                else
                {
                    ok = med;
                }
            }

            if (ok == -1)
            {
                return Parent(a);
            }

            return LowestCommonAncestor(_ancestor[a][ok], _ancestor[b][ok]);
        }

        public int Dist(int a, int b)
        {
            return Depth(a) + Depth(b) - 2 * Depth(LowestCommonAncestor(a, b));
        }
    }
}

namespace CompLib.Util
{
    using System;
    using System.Linq;

    class Scanner
    {
        private string[] _line;
        private int _index;
        private const char Separator = ' ';

        public Scanner()
        {
            _line = new string[0];
            _index = 0;
        }

        public string Next()
        {
            while (_index >= _line.Length)
            {
                _line = Console.ReadLine().Split(Separator);
                _index = 0;
            }

            return _line[_index++];
        }

        public int NextInt() => int.Parse(Next());
        public long NextLong() => long.Parse(Next());
        public double NextDouble() => double.Parse(Next());
        public decimal NextDecimal() => decimal.Parse(Next());
        public char NextChar() => Next()[0];
        public char[] NextCharArray() => Next().ToCharArray();

        public string[] Array()
        {
            _line = Console.ReadLine().Split(Separator);
            _index = _line.Length;
            return _line;
        }

        public int[] IntArray() => Array().Select(int.Parse).ToArray();
        public long[] LongArray() => Array().Select(long.Parse).ToArray();
        public double[] DoubleArray() => Array().Select(double.Parse).ToArray();
        public decimal[] DecimalArray() => Array().Select(decimal.Parse).ToArray();
    }
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	public void Solve(){

		var E = new List<int>[N];
		for(int i=0;i<N;i++) E[i] = new List<int>();
		for(int i=0;i<M;i++){
			E[U[i]].Add(i);
			E[V[i]].Add(i);
		}

		int[] depth = new int[N];
		int[] parent = new int[N];
		for(int i=0;i<N;i++){
			depth[i] = -1;
			parent[i] = -1;
		}

		bool[] usedEdge = new bool[M];
		int[] lastEdge = new int[N];
		for(int i=0;i<N;i++) lastEdge[i] = -1;

		var stk = new Stack<int>();
		stk.Push(0);
		//depth[0] = 0;
		while(stk.Count > 0){
			var now = stk.Pop();
			if(depth[now] != -1) continue;
			depth[now] = parent[now] == -1 ? 0 : (depth[parent[now]] + 1);
			if(lastEdge[now] != -1) usedEdge[lastEdge[now]] = true;

			foreach(var e in E[now]){
				int nxt = U[e] == now ? V[e] : U[e];
				if(nxt == parent[now]) continue;
				if(depth[nxt] != -1) continue;
				parent[nxt] = now;
				lastEdge[nxt] = e;
				stk.Push(nxt);
			}
		}

		//Console.WriteLine(String.Join(" ", usedEdge));
		//Console.WriteLine(String.Join(" ",depth));
		int minc = (int) 1e9;
		int midx = -1;
		for(int i=0;i<M;i++){
			if(usedEdge[i]) continue;
			int bottom = depth[U[i]] < depth[V[i]] ? V[i] : U[i];
			int top = bottom == U[i] ? V[i] : U[i];
			//Console.WriteLine("top: {0}, {1}, bottom:{2}, {3}",top, depth[top], bottom, depth[bottom]);
			int c = depth[bottom] - depth[top] + 1;
			if(minc > c){
				midx = i;
				minc = c;
			}
		}
		//Console.WriteLine("midx:{0}, minc: {1}",midx, minc);

		if(midx == -1){
			// Tree:
			List<int>[] b = new List<int>[2];
			for(int i=0;i<2;i++) b[i] = new List<int>();
			for(int i=0;i<N;i++){
				b[depth[i] % 2].Add(i + 1);
			}

			Array.Sort(b, (x, y) => x.Count.CompareTo(y.Count));
			List<int> ans = new List<int>();
			for(int i=0;i<K/2;i++) ans.Add(b[1][i]);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
			return;
		}

		// take minimal cycle:
		List<int> cyc = new List<int>();
		{
			int bottom = depth[U[midx]] < depth[V[midx]] ? V[midx] : U[midx];
			int top = bottom == U[midx] ? V[midx] : U[midx];
			int now = bottom;
			while(true){
				cyc.Add(now + 1);
				if(now == top) break;
				now = parent[now];
			}
		}

		if(cyc.Count <= K){
			Console.WriteLine(2);
			Console.WriteLine(cyc.Count);
			Console.WriteLine(String.Join(" ",cyc));
		} else {
			List<int> ans = new List<int>();
			for(int i=0;i<cyc.Count;i+=2) ans.Add(cyc[i]);
			while(ans.Count > 0 && ans.Count != (K + 1) / 2) ans.RemoveAt(ans.Count - 1);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
		}

	}
	int N,M,K;
	int[] U,V;
	public Sol(){
		var d = ria();
		N = d[0]; M = d[1]; K = d[2];
		U = new int[M];
		V = new int[M];
		for(int i=0;i<M;i++){
			d = ria();
			U[i] = d[0] - 1;
			V[i] = d[1] - 1;
		}
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	public void Solve(){

		var E = new List<int>[N];
		for(int i=0;i<N;i++) E[i] = new List<int>();
		for(int i=0;i<M;i++){
			E[U[i]].Add(i);
			E[V[i]].Add(i);
		}

		int lmax = 24;
		int[] depth = new int[N];
		int[][] parent = new int[lmax][];
		for(int i=0;i<N;i++){
			depth[i] = -1;
			parent[i] = new int[N];
			for(int j=0;j<N;j++){
				parent[i][j] = -1;
			}
		}
		bool[] ue = new bool[M];
		var q = new Queue<int>();
		q.Enqueue(0);
		depth[0] = 0;
		while(q.Count > 0){
			var now = q.Dequeue();
			foreach(var e in E[now]){
				int nxt = U[e] == now ? V[e]: U[e];
				if(parent[0][now] == nxt) continue;
				if(depth[nxt] == -1){
					ue[e] = true;
					depth[nxt] = depth[now] + 1;
					parent[0][nxt] = now;
					q.Enqueue(nxt);
				}
			}
		}

		bool bip = true;
		for(int i=0;i<M;i++){
			bip &= ((depth[U[i]] + depth[V[i]]) % 2 == 1);
		}
		if(bip){
			List<int>[] b = new List<int>[2];
			for(int i=0;i<2;i++) b[i] = new List<int>();
			for(int i=0;i<N;i++){
				b[depth[i] % 2].Add(i + 1);
			}

			Array.Sort(b, (x, y) => x.Count.CompareTo(y.Count));
			List<int> ans = new List<int>();
			for(int i=0;i<K/2;i++) ans.Add(b[1][i]);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
			return;
		}

		for(int i=0;i<lmax-1;i++){
			for(int j=0;j<N;j++){
				if(parent[i][j]<0){
					parent[i+1][j]=-1;
				}else{
					parent[i+1][j]=parent[i][parent[i][j]];
				}
			}
		}

		Func<int, int, int> lca = null;
		lca = (u, v) => {
			if(depth[u]>depth[v])return lca(v,u);
			for(int i=0;i<lmax;i++){
				if( (((depth[v]-depth[u])>>i)&1) >0 ){
					v=parent[i][v];
				}
			}
			if(u==v)return u;
			for(int i=lmax-1;i>=0;i--){
				if(parent[i][u]!=parent[i][v]){
					u=parent[i][u];
					v=parent[i][v];
				}
			}
			return parent[0][u];

		};

		int minc = (int) 1e9;
		int midx = -1;
		for(int i=0;i<M;i++){
			if(ue[i]) continue;
			int c = depth[U[i]] + depth[V[i]] - 2 * depth[lca(U[i], V[i])] + 1;
			if(c < minc){
				minc = c;
				midx = i;
			}
		}

		var l0 = lca(U[midx], V[midx]);
		List<int> ll = new List<int>();
		int n = U[midx];
		while(n != l0){
			ll.Add(n);
			n = parent[0][n];
		}
		List<int> rr = new List<int>();
		n = V[midx];
		while(n != l0){
			rr.Add(n);
			n = parent[0][n];
		}

		ll.Add(l0);
		foreach(var m in rr) ll.Add(m);

		if(ll.Count <= K){
			Console.WriteLine(2);
			Console.WriteLine(String.Join(" ",ll));
		} else {
			List<int> ans = new List<int>();
			for(int i=0;i<ll.Count;i+=2) ans.Add(ll[i]);
			while(ans.Count > 0 && ans.Count != K / 2) ans.RemoveAt(ans.Count - 1);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
		}

	}
	int N,M,K;
	int[] U,V;
	public Sol(){
		var d = ria();
		N = d[0]; M = d[1]; K = d[1];
		U = new int[M];
		V = new int[M];
		for(int i=0;i<M;i++){
			d = ria();
			U[i] = d[0] - 1;
			V[i] = d[1] - 1;
		}
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	public void Solve(){

		var E = new List<int>[N];
		for(int i=0;i<N;i++) E[i] = new List<int>();
		for(int i=0;i<M;i++){
			E[U[i]].Add(i);
			E[V[i]].Add(i);
		}

		int[] depth = new int[N];
		int[] parent = new int[N];
		for(int i=0;i<N;i++){
			depth[i] = -1;
			parent[i] = -1;
		}

		bool[] usedEdge = new bool[M];
		int[] lastEdge = new int[N];
		for(int i=0;i<N;i++) lastEdge[i] = -1;

		var stk = new Stack<int>();
		stk.Push(0);
		//depth[0] = 0;
		while(stk.Count > 0){
			var now = stk.Pop();
			if(depth[now] != -1) continue;
			depth[now] = parent[now] == -1 ? 0 : (depth[parent[now]] + 1);
			if(lastEdge[now] != -1) usedEdge[lastEdge[now]] = true;

			foreach(var e in E[now]){
				int nxt = U[e] == now ? V[e] : U[e];
				if(nxt == parent[now]) continue;
				if(depth[nxt] != -1) continue;
				parent[nxt] = now;
				lastEdge[nxt] = e;
				stk.Push(nxt);
			}
		}

		//Console.WriteLine(String.Join(" ", usedEdge));
		Console.WriteLine(String.Join(" ",depth));
		int minc = (int) 1e9;
		int midx = -1;
		for(int i=0;i<M;i++){
			if(usedEdge[i]) continue;
			int bottom = depth[U[i]] < depth[V[i]] ? V[i] : U[i];
			int top = bottom == U[i] ? V[i] : U[i];
			//Console.WriteLine("top: {0}, {1}, bottom:{2}, {3}",top, depth[top], bottom, depth[bottom]);
			int c = depth[bottom] - depth[top] + 1;
			if(minc > c){
				midx = i;
				minc = c;
			}
		}
		//Console.WriteLine("midx:{0}, minc: {1}",midx, minc);

		if(midx == -1){
			// Tree:
			List<int>[] b = new List<int>[2];
			for(int i=0;i<2;i++) b[i] = new List<int>();
			for(int i=0;i<N;i++){
				b[depth[i] % 2].Add(i + 1);
			}

			Array.Sort(b, (x, y) => x.Count.CompareTo(y.Count));
			List<int> ans = new List<int>();
			for(int i=0;i<K/2;i++) ans.Add(b[1][i]);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
			return;
		}

		// take minimal cycle:
		List<int> cyc = new List<int>();
		{
			int bottom = depth[U[midx]] < depth[V[midx]] ? V[midx] : U[midx];
			int top = bottom == U[midx] ? V[midx] : U[midx];
			int now = bottom;
			while(true){
				cyc.Add(now + 1);
				if(now == top) break;
				now = parent[now];
			}
		}

		if(cyc.Count <= K){
			Console.WriteLine(2);
			Console.WriteLine(cyc.Count);
			Console.WriteLine(String.Join(" ",cyc));
		} else {
			List<int> ans = new List<int>();
			for(int i=0;i<cyc.Count;i+=2) ans.Add(cyc[i]);
			while(ans.Count > 0 && ans.Count != (K + 1) / 2) ans.RemoveAt(ans.Count - 1);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
		}

	}
	int N,M,K;
	int[] U,V;
	public Sol(){
		var d = ria();
		N = d[0]; M = d[1]; K = d[2];
		U = new int[M];
		V = new int[M];
		for(int i=0;i<M;i++){
			d = ria();
			U[i] = d[0] - 1;
			V[i] = d[1] - 1;
		}
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	public void Solve(){

		var E = new List<int>[N];
		for(int i=0;i<N;i++) E[i] = new List<int>();
		for(int i=0;i<M;i++){
			E[U[i]].Add(i);
			E[V[i]].Add(i);
		}

		int lmax = 24;
		int[] depth = new int[N];
		int[][] parent = new int[lmax][];
		for(int i=0;i<N;i++){
			depth[i] = -1;
			parent[i] = new int[N];
			for(int j=0;j<N;j++){
				parent[i][j] = -1;
			}
		}
		bool[] ue = new bool[M];
		var q = new Queue<int>();
		q.Enqueue(0);
		depth[0] = 0;
		while(q.Count > 0){
			var now = q.Dequeue();
			foreach(var e in E[now]){
				int nxt = U[e] == now ? V[e]: U[e];
				if(parent[0][now] == nxt) continue;
				if(depth[nxt] == -1){
					ue[e] = true;
					depth[nxt] = depth[now] + 1;
					parent[0][nxt] = now;
					q.Enqueue(nxt);
				}
			}
		}

		bool bip = true;
		for(int i=0;i<M;i++){
			bip &= ((depth[U[i]] + depth[V[i]]) % 2 == 1);
		}
		if(bip){
			List<int>[] b = new List<int>[2];
			for(int i=0;i<2;i++) b[i] = new List<int>();
			for(int i=0;i<N;i++){
				b[depth[i] % 2].Add(i + 1);
			}

			Array.Sort(b, (x, y) => x.Count.CompareTo(y.Count));
			List<int> ans = new List<int>();
			for(int i=0;i<K/2;i++) ans.Add(b[1][i]);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
			return;
		}

		for(int i=0;i<lmax-1;i++){
			for(int j=0;j<N;j++){
				if(parent[i][j]<0){
					parent[i+1][j]=-1;
				}else{
					parent[i+1][j]=parent[i][parent[i][j]];
				}
			}
		}

		Func<int, int, int> lca = null;
		lca = (u, v) => {
			if(depth[u]>depth[v])return lca(v,u);
			for(int i=0;i<lmax;i++){
				if( (((depth[v]-depth[u])>>i)&1) >0 ){
					v=parent[i][v];
				}
			}
			if(u==v)return u;
			for(int i=lmax-1;i>=0;i--){
				if(parent[i][u]!=parent[i][v]){
					u=parent[i][u];
					v=parent[i][v];
				}
			}
			return parent[0][u];

		};

		int minc = (int) 1e9;
		int midx = -1;
		for(int i=0;i<M;i++){
			if(ue[i]) continue;
			int c = depth[U[i]] + depth[V[i]] - 2 * depth[lca(U[i], V[i])] + 1;
			if(c < minc){
				minc = c;
				midx = i;
			}
		}

		var l0 = lca(U[midx], V[midx]);
		List<int> ll = new List<int>();
		int n = U[midx];
		while(n != l0){
			ll.Add(n);
			n = parent[0][n];
		}
		List<int> rr = new List<int>();
		n = V[midx];
		while(n != l0){
			rr.Add(n);
			n = parent[0][n];
		}

		ll.Add(l0);
		foreach(var m in rr) ll.Add(m);

		if(ll.Count <= K){
			Console.WriteLine(2);
			Console.WriteLine(ll.Count);
			Console.WriteLine(String.Join(" ",ll));
		} else {
			List<int> ans = new List<int>();
			for(int i=0;i<ll.Count;i+=2) ans.Add(ll[i]);
			while(ans.Count > 0 && ans.Count != K / 2) ans.RemoveAt(ans.Count - 1);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
		}

	}
	int N,M,K;
	int[] U,V;
	public Sol(){
		var d = ria();
		N = d[0]; M = d[1]; K = d[1];
		U = new int[M];
		V = new int[M];
		for(int i=0;i<M;i++){
			d = ria();
			U[i] = d[0] - 1;
			V[i] = d[1] - 1;
		}
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	public void Solve(){

		var E = new List<int>[N];
		for(int i=0;i<N;i++) E[i] = new List<int>();
		for(int i=0;i<M;i++){
			E[U[i]].Add(i);
			E[V[i]].Add(i);
		}

		int[] depth = new int[N];
		int[] parent = new int[N];
		for(int i=0;i<N;i++){
			depth[i] = -1;
			parent[i] = -1;
		}

		bool[] usedEdge = new bool[M];
		int[] lastEdge = new int[N];
		for(int i=0;i<N;i++) lastEdge[i] = -1;

		var stk = new Stack<int>();
		stk.Push(0);
		//depth[0] = 0;
		while(stk.Count > 0){
			var now = stk.Pop();
			if(depth[now] != -1) continue;
			depth[now] = parent[now] == -1 ? 0 : (depth[parent[now]] + 1);
			if(lastEdge[now] != -1) usedEdge[lastEdge[now]] = true;

			foreach(var e in E[now]){
				int nxt = U[e] == now ? V[e] : U[e];
				if(nxt == parent[now]) continue;
				if(depth[nxt] != -1) continue;
				parent[nxt] = now;
				lastEdge[nxt] = e;
				stk.Push(nxt);
			}
		}

		//Console.WriteLine(String.Join(" ", usedEdge));
		//Console.WriteLine(String.Join(" ",depth));
		int minc = (int) 1e9;
		int midx = -1;
		for(int i=0;i<M;i++){
			if(usedEdge[i]) continue;
			int bottom = depth[U[i]] < depth[V[i]] ? V[i] : U[i];
			int top = bottom == U[i] ? V[i] : U[i];
			//Console.WriteLine("top: {0}, {1}, bottom:{2}, {3}",top, depth[top], bottom, depth[bottom]);
			int c = depth[bottom] - depth[top] + 1;
			if(minc > c){
				midx = i;
				minc = c;
			}
		}
		//Console.WriteLine("midx:{0}, minc: {1}",midx, minc);

		if(midx == -1){
			// Tree:
			List<int>[] b = new List<int>[2];
			for(int i=0;i<2;i++) b[i] = new List<int>();
			for(int i=0;i<N;i++){
				b[depth[i] % 2].Add(i + 1);
			}

			Array.Sort(b, (x, y) => x.Count.CompareTo(y.Count));
			List<int> ans = new List<int>();
			for(int i=0;i<(K+1)/2;i++) ans.Add(b[1][i]);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
			return;
		}

		// take minimal cycle:
		List<int> cyc = new List<int>();
		{
			int bottom = depth[U[midx]] < depth[V[midx]] ? V[midx] : U[midx];
			int top = bottom == U[midx] ? V[midx] : U[midx];
			int now = bottom;
			while(true){
				cyc.Add(now + 1);
				if(now == top) break;
				now = parent[now];
			}
		}

		if(cyc.Count <= K){
			Console.WriteLine(2);
			Console.WriteLine(cyc.Count);
			Console.WriteLine(String.Join(" ",cyc));
		} else {
			List<int> ans = new List<int>();
			for(int i=0;i<cyc.Count;i+=2) ans.Add(cyc[i]);
			while(ans.Count > 0 && ans.Count != (K + 1) / 2) ans.RemoveAt(ans.Count - 1);
			Console.WriteLine(1);
			Console.WriteLine(String.Join(" ",ans));
		}

	}
	int N,M,K;
	int[] U,V;
	public Sol(){
		var d = ria();
		N = d[0]; M = d[1]; K = d[2];
		U = new int[M];
		V = new int[M];
		for(int i=0;i<M;i++){
			d = ria();
			U[i] = d[0] - 1;
			V[i] = d[1] - 1;
		}
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static System.Math;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Runtime.CompilerServices;
using Library;

namespace Program
{
    public static class ProblemD
    {
        static bool SAIKI = false;
        static public int numberOfRandomCases = 0;
        static public void MakeTestCase(List<string> _input, List<string> _output, ref Func<string[], bool> _outputChecker)
        {
        }
        static public void Solve()
        {
            var n = NN;
            var m = NN;
            var k = NN;
            var uv = Repeat(0, m).Select(_ => new { u = NN - 1, v = NN - 1 }).ToArray();
            var path = Repeat(0, n).Select(_ => new List<long>()).ToArray();
            var bu = new long[n];
            for (var i = 0; i < n; i++)
            {
                bu[i] = -1;
            }
            foreach (var item in uv)
            {
                path[item.u].Add(item.v);
                path[item.v].Add(item.u);
            }
            var isNibu = true;
            var pred = new long[n];
            {
                var done = new bool[n];
                var q = new Queue<long>();
                done[0] = true;
                pred[0] = 0;
                bu[0] = 0;
                q.Enqueue(0);
                var badList = new List<long>();
                while (q.Count > 0)
                {
                    var v = q.Dequeue();
                    foreach (var item in path[v])
                    {
                        if (done[item])
                        {
                            if (bu[item] != 1 - bu[v])
                            {
                                badList.Add(v);
                            }
                            continue;
                        }
                        pred[item] = v;
                        done[item] = true;
                        q.Enqueue(item);

                        bu[item] = 1 - bu[v];
                    }
                }
                foreach (var item in badList)
                {
                    bu[item] = 2;
                }
            }
            var acnt = bu.Count(e => e == 0);
            var bcnt = bu.Count(e => e == 1);
            var exactly = (k - 1) / 2 + 1;
            if (Max(acnt, bcnt) >= exactly)
            {
                Console.WriteLine(1);
                if (acnt > bcnt)
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 0) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
                else
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 1) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
            }
            else
            {
                Console.WriteLine(2);
                var ans = new List<long>();
                {
                    var done = new bool[n];
                    done[0] = true;
                    var dist = new long[n];
                    dist[0] = 0;
                    var pred2 = new long[n];
                    pred2[0] = 0;
                    var ok = false;
                    Action<long> act = null;
                    act = v =>
                    {
                        foreach (var item in path[v])
                        {
                            if (ok) break;
                            if (done[item])
                            {
                                if (dist[v] - dist[item] > 1)
                                {
                                    var len = dist[v] - dist[item] + 1;
                                    if (len <= k)
                                    {
                                        while (v != item)
                                        {
                                            ans.Add(v + 1);
                                            v = pred2[v];
                                        }
                                        ans.Add(item + 1);
                                        ok = true;
                                        break;
                                    }
                                }
                                continue;
                            }
                            dist[item] = dist[v] + 1;
                            pred2[item] = v;
                            done[item] = true;
                            act(item);
                        }
                    };
                    act(0);
                }
                Console.WriteLine(ans.Count);
                Console.WriteLine(string.Join(" ", ans));
            }
        }
        class Printer : StreamWriter
        {
            public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } }
            public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { base.AutoFlush = false; }
            public Printer(Stream stream, Encoding encoding) : base(stream, encoding) { base.AutoFlush = false; }
        }
        static LIB_FastIO fastio = new LIB_FastIODebug();
        static public void Main(string[] args) { if (args.Length == 0) { fastio = new LIB_FastIO(); Console.SetOut(new Printer(Console.OpenStandardOutput())); } if (SAIKI) { var t = new Thread(Solve, 134217728); t.Start(); t.Join(); } else Solve(); Console.Out.Flush(); }
        static long NN => fastio.Long();
        static double ND => fastio.Double();
        static string NS => fastio.Scan();
        static long[] NNList(long N) => Repeat(0, N).Select(_ => NN).ToArray();
        static double[] NDList(long N) => Repeat(0, N).Select(_ => ND).ToArray();
        static string[] NSList(long N) => Repeat(0, N).Select(_ => NS).ToArray();
        static long Count<T>(this IEnumerable<T> x, Func<T, bool> pred) => Enumerable.Count(x, pred);
        static IEnumerable<T> Repeat<T>(T v, long n) => Enumerable.Repeat<T>(v, (int)n);
        static IEnumerable<int> Range(long s, long c) => Enumerable.Range((int)s, (int)c);
        static IOrderedEnumerable<T> OrderByRand<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x, _ => xorshift);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderBy<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderBy(x.OrderByRand(), selector);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x) => Enumerable.OrderByDescending(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderByDescending<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderByDescending(x.OrderByRand(), selector);
        static IOrderedEnumerable<string> OrderBy(this IEnumerable<string> x) => x.OrderByRand().OrderBy(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderBy(selector, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<string> OrderByDescending(this IEnumerable<string> x) => x.OrderByRand().OrderByDescending(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderByDescending(selector, StringComparer.OrdinalIgnoreCase);
        static uint xorshift { get { _xsi.MoveNext(); return _xsi.Current; } }
        static IEnumerator<uint> _xsi = _xsc();
        static IEnumerator<uint> _xsc() { uint x = 123456789, y = 362436069, z = 521288629, w = (uint)(DateTime.Now.Ticks & 0xffffffff); while (true) { var t = x ^ (x << 11); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); yield return w; } }
    }
}
namespace Library {
    class LIB_FastIO
    {
        public LIB_FastIO() { str = Console.OpenStandardInput(); }
        readonly Stream str;
        readonly byte[] buf = new byte[1024];
        int len, ptr;
        public bool isEof = false;
        public bool IsEndOfStream { get { return isEof; } }
        byte read()
        {
            if (isEof) throw new EndOfStreamException();
            if (ptr >= len)
            {
                ptr = 0;
                if ((len = str.Read(buf, 0, 1024)) <= 0)
                {
                    isEof = true;
                    return 0;
                }
            }
            return buf[ptr++];
        }
        char Char()
        {
            byte b = 0;
            do b = read();
            while (b < 33 || 126 < b);
            return (char)b;
        }
        virtual public string Scan()
        {
            var sb = new StringBuilder();
            for (var b = Char(); b >= 33 && b <= 126; b = (char)read())
                sb.Append(b);
            return sb.ToString();
        }
        virtual public long Long()
        {
            long ret = 0; byte b = 0; var ng = false;
            do b = read();
            while (b != '-' && (b < '0' || '9' < b));
            if (b == '-') { ng = true; b = read(); }
            for (; true; b = read())
            {
                if (b < '0' || '9' < b)
                    return ng ? -ret : ret;
                else ret = ret * 10 + b - '0';
            }
        }
        virtual public double Double() { return double.Parse(Scan(), CultureInfo.InvariantCulture); }
    }
    class LIB_FastIODebug : LIB_FastIO
    {
        Queue<string> param = new Queue<string>();
        string NextString() { if (param.Count == 0) foreach (var item in Console.ReadLine().Split(' ')) param.Enqueue(item); return param.Dequeue(); }
        public LIB_FastIODebug() { }
        public override string Scan() => NextString();
        public override long Long() => long.Parse(NextString());
        public override double Double() => double.Parse(NextString());
    }
}


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static System.Math;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Runtime.CompilerServices;
using Library;

namespace Program
{
    public static class ProblemD
    {
        static bool SAIKI = true;
        static public int numberOfRandomCases = 0;
        static public void MakeTestCase(List<string> _input, List<string> _output, ref Func<string[], bool> _outputChecker)
        {
        }
        static public void Solve()
        {
            var n = NN;
            var m = NN;
            var k = NN;
            var uv = Repeat(0, m).Select(_ => new { u = NN - 1, v = NN - 1 }).ToArray();
            var path = Repeat(0, n).Select(_ => new List<long>()).ToArray();
            var bu = new long[n];
            for (var i = 0; i < n; i++)
            {
                bu[i] = -1;
            }
            foreach (var item in uv)
            {
                path[item.u].Add(item.v);
                path[item.v].Add(item.u);
            }
            var isNibu = true;
            var pred = new long[n];
            {
                var done = new bool[n];
                var q = new Queue<long>();
                done[0] = true;
                pred[0] = 0;
                bu[0] = 0;
                q.Enqueue(0);
                var badList = new List<long>();
                while (q.Count > 0)
                {
                    var v = q.Dequeue();
                    foreach (var item in path[v])
                    {
                        if (done[item])
                        {
                            if (bu[item] != 1 - bu[v])
                            {
                                badList.Add(v);
                            }
                            continue;
                        }
                        pred[item] = v;
                        done[item] = true;
                        q.Enqueue(item);

                        bu[item] = 1 - bu[v];
                    }
                }
                foreach (var item in badList)
                {
                    bu[item] = 2;
                }
            }
            var acnt = bu.Count(e => e == 0);
            var bcnt = bu.Count(e => e == 1);
            var exactly = (k - 1) / 2 + 1;
            if (Max(acnt, bcnt) >= exactly)
            {
                Console.WriteLine(1);
                if (acnt > bcnt)
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 0) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
                else
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 1) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
            }
            else
            {
                Console.WriteLine(2);
                var ans = new List<long>();
                {
                    var done = new bool[n];
                    done[0] = true;
                    var dist = new long[n];
                    dist[0] = 0;
                    var pred2 = new long[n];
                    pred2[0] = 0;
                    var ok = false;
                    Action<long> act = null;
                    act = v =>
                    {
                        foreach (var item in path[v])
                        {
                            if (ok) break;
                            if (done[item])
                            {
                                if (dist[v] - dist[item] > 1)
                                {
                                    var len = dist[v] - dist[item] + 1;
                                    if (len <= k)
                                    {
                                        while (v != item)
                                        {
                                            ans.Add(v + 1);
                                            v = pred2[v];
                                        }
                                        ans.Add(item + 1);
                                        ok = true;
                                        break;
                                    }
                                }
                                continue;
                            }
                            dist[item] = dist[v] + 1;
                            pred2[item] = v;
                            done[item] = true;
                            act(item);
                        }
                    };
                    act(0);
                }
                Console.WriteLine(ans.Count);
                Console.WriteLine(string.Join(" ", ans));
            }
        }
        class Printer : StreamWriter
        {
            public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } }
            public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { base.AutoFlush = false; }
            public Printer(Stream stream, Encoding encoding) : base(stream, encoding) { base.AutoFlush = false; }
        }
        static LIB_FastIO fastio = new LIB_FastIODebug();
        static public void Main(string[] args) { if (args.Length == 0) { fastio = new LIB_FastIO(); Console.SetOut(new Printer(Console.OpenStandardOutput())); } if (SAIKI) { var t = new Thread(Solve, 134217728); t.Start(); t.Join(); } else Solve(); Console.Out.Flush(); }
        static long NN => fastio.Long();
        static double ND => fastio.Double();
        static string NS => fastio.Scan();
        static long[] NNList(long N) => Repeat(0, N).Select(_ => NN).ToArray();
        static double[] NDList(long N) => Repeat(0, N).Select(_ => ND).ToArray();
        static string[] NSList(long N) => Repeat(0, N).Select(_ => NS).ToArray();
        static long Count<T>(this IEnumerable<T> x, Func<T, bool> pred) => Enumerable.Count(x, pred);
        static IEnumerable<T> Repeat<T>(T v, long n) => Enumerable.Repeat<T>(v, (int)n);
        static IEnumerable<int> Range(long s, long c) => Enumerable.Range((int)s, (int)c);
        static IOrderedEnumerable<T> OrderByRand<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x, _ => xorshift);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderBy<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderBy(x.OrderByRand(), selector);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x) => Enumerable.OrderByDescending(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderByDescending<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderByDescending(x.OrderByRand(), selector);
        static IOrderedEnumerable<string> OrderBy(this IEnumerable<string> x) => x.OrderByRand().OrderBy(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderBy(selector, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<string> OrderByDescending(this IEnumerable<string> x) => x.OrderByRand().OrderByDescending(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderByDescending(selector, StringComparer.OrdinalIgnoreCase);
        static uint xorshift { get { _xsi.MoveNext(); return _xsi.Current; } }
        static IEnumerator<uint> _xsi = _xsc();
        static IEnumerator<uint> _xsc() { uint x = 123456789, y = 362436069, z = 521288629, w = (uint)(DateTime.Now.Ticks & 0xffffffff); while (true) { var t = x ^ (x << 11); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); yield return w; } }
    }
}
namespace Library {
    class LIB_FastIO
    {
        public LIB_FastIO() { str = Console.OpenStandardInput(); }
        readonly Stream str;
        readonly byte[] buf = new byte[1024];
        int len, ptr;
        public bool isEof = false;
        public bool IsEndOfStream { get { return isEof; } }
        byte read()
        {
            if (isEof) throw new EndOfStreamException();
            if (ptr >= len)
            {
                ptr = 0;
                if ((len = str.Read(buf, 0, 1024)) <= 0)
                {
                    isEof = true;
                    return 0;
                }
            }
            return buf[ptr++];
        }
        char Char()
        {
            byte b = 0;
            do b = read();
            while (b < 33 || 126 < b);
            return (char)b;
        }
        virtual public string Scan()
        {
            var sb = new StringBuilder();
            for (var b = Char(); b >= 33 && b <= 126; b = (char)read())
                sb.Append(b);
            return sb.ToString();
        }
        virtual public long Long()
        {
            long ret = 0; byte b = 0; var ng = false;
            do b = read();
            while (b != '-' && (b < '0' || '9' < b));
            if (b == '-') { ng = true; b = read(); }
            for (; true; b = read())
            {
                if (b < '0' || '9' < b)
                    return ng ? -ret : ret;
                else ret = ret * 10 + b - '0';
            }
        }
        virtual public double Double() { return double.Parse(Scan(), CultureInfo.InvariantCulture); }
    }
    class LIB_FastIODebug : LIB_FastIO
    {
        Queue<string> param = new Queue<string>();
        string NextString() { if (param.Count == 0) foreach (var item in Console.ReadLine().Split(' ')) param.Enqueue(item); return param.Dequeue(); }
        public LIB_FastIODebug() { }
        public override string Scan() => NextString();
        public override long Long() => long.Parse(NextString());
        public override double Double() => double.Parse(NextString());
    }
}


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static System.Math;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Runtime.CompilerServices;
using Library;

namespace Program
{
    public static class ProblemD
    {
        static bool SAIKI = false;
        static public int numberOfRandomCases = 0;
        static public void MakeTestCase(List<string> _input, List<string> _output, ref Func<string[], bool> _outputChecker)
        {
        }
        static public void Solve()
        {
            var n = NN;
            var m = NN;
            var k = NN;
            var uv = Repeat(0, m).Select(_ => new { u = NN - 1, v = NN - 1 }).ToArray();
            var path = Repeat(0, n).Select(_ => new List<long>()).ToArray();
            var bu = new long[n];
            for (var i = 0; i < n; i++)
            {
                bu[i] = -1;
            }
            foreach (var item in uv)
            {
                path[item.u].Add(item.v);
                path[item.v].Add(item.u);
            }
            var isNibu = true;
            var pred = new long[n];
            var endV1 = -1L;
            var endV2 = -1L;
            {
                var done = new bool[n];
                var q = new Queue<long>();
                done[0] = true;
                pred[0] = 0;
                bu[0] = 0;
                q.Enqueue(0);
                while (q.Count > 0)
                {
                    var v = q.Dequeue();
                    foreach (var item in path[v])
                    {
                        if (done[item])
                        {
                            if (bu[item] != 1 - bu[v])
                            {
                                isNibu = false;
                                endV1 = v;
                                endV2 = item;
                                break;
                            }
                            continue;
                        }
                        pred[item] = v;
                        done[item] = true;
                        q.Enqueue(item);

                        bu[item] = 1 - bu[v];
                    }
                    if (!isNibu) break;
                }
            }
            if (isNibu)
            {
                Console.WriteLine(1);
                var acnt = bu.Count(e => e == 0);
                var bcnt = bu.Count(e => e == 1);
                var exactly = (k - 1) / 2 + 1;
                if (acnt > bcnt)
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 0) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
                else
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 1) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
            }
            else
            {
                Console.WriteLine(2);
                var ans = new List<long>();
                while (endV1 != 0)
                {
                    ans.Add(endV1 + 1);
                    endV1 = pred[endV1];
                }
                ans.Add(1);
                ans.Reverse();
                while (endV2 != 0)
                {
                    ans.Add(endV2 + 1);
                    endV2 = pred[endV2];
                }
                Console.WriteLine(ans.Count);
                Console.WriteLine(string.Join(" ", ans));
            }
        }
        class Printer : StreamWriter
        {
            public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } }
            public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { base.AutoFlush = false; }
            public Printer(Stream stream, Encoding encoding) : base(stream, encoding) { base.AutoFlush = false; }
        }
        static LIB_FastIO fastio = new LIB_FastIODebug();
        static public void Main(string[] args) { if (args.Length == 0) { fastio = new LIB_FastIO(); Console.SetOut(new Printer(Console.OpenStandardOutput())); } if (SAIKI) { var t = new Thread(Solve, 134217728); t.Start(); t.Join(); } else Solve(); Console.Out.Flush(); }
        static long NN => fastio.Long();
        static double ND => fastio.Double();
        static string NS => fastio.Scan();
        static long[] NNList(long N) => Repeat(0, N).Select(_ => NN).ToArray();
        static double[] NDList(long N) => Repeat(0, N).Select(_ => ND).ToArray();
        static string[] NSList(long N) => Repeat(0, N).Select(_ => NS).ToArray();
        static long Count<T>(this IEnumerable<T> x, Func<T, bool> pred) => Enumerable.Count(x, pred);
        static IEnumerable<T> Repeat<T>(T v, long n) => Enumerable.Repeat<T>(v, (int)n);
        static IEnumerable<int> Range(long s, long c) => Enumerable.Range((int)s, (int)c);
        static IOrderedEnumerable<T> OrderByRand<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x, _ => xorshift);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderBy<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderBy(x.OrderByRand(), selector);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x) => Enumerable.OrderByDescending(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderByDescending<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderByDescending(x.OrderByRand(), selector);
        static IOrderedEnumerable<string> OrderBy(this IEnumerable<string> x) => x.OrderByRand().OrderBy(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderBy(selector, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<string> OrderByDescending(this IEnumerable<string> x) => x.OrderByRand().OrderByDescending(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderByDescending(selector, StringComparer.OrdinalIgnoreCase);
        static uint xorshift { get { _xsi.MoveNext(); return _xsi.Current; } }
        static IEnumerator<uint> _xsi = _xsc();
        static IEnumerator<uint> _xsc() { uint x = 123456789, y = 362436069, z = 521288629, w = (uint)(DateTime.Now.Ticks & 0xffffffff); while (true) { var t = x ^ (x << 11); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); yield return w; } }
    }
}
namespace Library {
    class LIB_FastIO
    {
        public LIB_FastIO() { str = Console.OpenStandardInput(); }
        readonly Stream str;
        readonly byte[] buf = new byte[1024];
        int len, ptr;
        public bool isEof = false;
        public bool IsEndOfStream { get { return isEof; } }
        byte read()
        {
            if (isEof) throw new EndOfStreamException();
            if (ptr >= len)
            {
                ptr = 0;
                if ((len = str.Read(buf, 0, 1024)) <= 0)
                {
                    isEof = true;
                    return 0;
                }
            }
            return buf[ptr++];
        }
        char Char()
        {
            byte b = 0;
            do b = read();
            while (b < 33 || 126 < b);
            return (char)b;
        }
        virtual public string Scan()
        {
            var sb = new StringBuilder();
            for (var b = Char(); b >= 33 && b <= 126; b = (char)read())
                sb.Append(b);
            return sb.ToString();
        }
        virtual public long Long()
        {
            long ret = 0; byte b = 0; var ng = false;
            do b = read();
            while (b != '-' && (b < '0' || '9' < b));
            if (b == '-') { ng = true; b = read(); }
            for (; true; b = read())
            {
                if (b < '0' || '9' < b)
                    return ng ? -ret : ret;
                else ret = ret * 10 + b - '0';
            }
        }
        virtual public double Double() { return double.Parse(Scan(), CultureInfo.InvariantCulture); }
    }
    class LIB_FastIODebug : LIB_FastIO
    {
        Queue<string> param = new Queue<string>();
        string NextString() { if (param.Count == 0) foreach (var item in Console.ReadLine().Split(' ')) param.Enqueue(item); return param.Dequeue(); }
        public LIB_FastIODebug() { }
        public override string Scan() => NextString();
        public override long Long() => long.Parse(NextString());
        public override double Double() => double.Parse(NextString());
    }
}


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static System.Math;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Runtime.CompilerServices;
using Library;

namespace Program
{
    public static class ProblemD
    {
        static bool SAIKI = false;
        static public int numberOfRandomCases = 0;
        static public void MakeTestCase(List<string> _input, List<string> _output, ref Func<string[], bool> _outputChecker)
        {
        }
        static public void Solve()
        {
            var n = NN;
            var m = NN;
            var k = NN;
            var uv = Repeat(0, m).Select(_ => new { u = NN - 1, v = NN - 1 }).ToArray();
            var path = Repeat(0, n).Select(_ => new List<long>()).ToArray();
            var bu = new long[n];
            for (var i = 0; i < n; i++)
            {
                bu[i] = -1;
            }
            foreach (var item in uv)
            {
                path[item.u].Add(item.v);
                path[item.v].Add(item.u);
            }
            var isNibu = true;
            var pred = new long[n];
            {
                var done = new bool[n];
                var q = new Queue<long>();
                done[0] = true;
                pred[0] = 0;
                bu[0] = 0;
                q.Enqueue(0);
                while (q.Count > 0)
                {
                    var v = q.Dequeue();
                    foreach (var item in path[v])
                    {
                        if (done[item])
                        {
                            if (bu[item] != 1 - bu[v])
                            {
                                bu[v] = 2;
                            }
                            continue;
                        }
                        pred[item] = v;
                        done[item] = true;
                        q.Enqueue(item);

                        bu[item] = 1 - bu[v];
                    }
                }
            }
            var acnt = bu.Count(e => e == 0);
            var bcnt = bu.Count(e => e == 1);
            var exactly = (k - 1) / 2 + 1;
            if (Max(acnt, bcnt) >= exactly)
            {
                Console.WriteLine(1);
                if (acnt > bcnt)
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 0) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
                else
                {
                    var ans = new List<long>();
                    for (var i = 0; i < n; i++)
                    {
                        if (bu[i] == 1) ans.Add(i + 1);
                    }
                    Console.WriteLine(string.Join(" ", ans.Take((int)exactly)));
                }
            }
            else
            {
                Console.WriteLine(2);
                var ans = new List<long>();
                {
                    var done = new bool[n];
                    done[0] = true;
                    var dist = new long[n];
                    dist[0] = 0;
                    var pred2 = new long[n];
                    pred2[0] = 0;
                    var ok = false;
                    Action<long> act = null;
                    act = v =>
                    {
                        foreach (var item in path[v])
                        {
                            if(ok) break;
                            if (done[item])
                            {
                                if (dist[v] - dist[item] > 1)
                                {
                                    var len = dist[v] - dist[item] + 1;
                                    if (len <= k)
                                    {
                                        while (v != item)
                                        {
                                            ans.Add(v + 1);
                                            v = pred2[v];
                                        }
                                        ans.Add(item + 1);
                                        ok = true;
                                        break;
                                    }
                                }
                                continue;
                            }
                            dist[item] = dist[v] + 1;
                            pred2[item] = v;
                            done[item] = true;
                            act(item);
                        }
                    };
                    act(0);
                }
                Console.WriteLine(ans.Count);
                Console.WriteLine(string.Join(" ", ans));
            }
        }
        class Printer : StreamWriter
        {
            public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } }
            public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { base.AutoFlush = false; }
            public Printer(Stream stream, Encoding encoding) : base(stream, encoding) { base.AutoFlush = false; }
        }
        static LIB_FastIO fastio = new LIB_FastIODebug();
        static public void Main(string[] args) { if (args.Length == 0) { fastio = new LIB_FastIO(); Console.SetOut(new Printer(Console.OpenStandardOutput())); } if (SAIKI) { var t = new Thread(Solve, 134217728); t.Start(); t.Join(); } else Solve(); Console.Out.Flush(); }
        static long NN => fastio.Long();
        static double ND => fastio.Double();
        static string NS => fastio.Scan();
        static long[] NNList(long N) => Repeat(0, N).Select(_ => NN).ToArray();
        static double[] NDList(long N) => Repeat(0, N).Select(_ => ND).ToArray();
        static string[] NSList(long N) => Repeat(0, N).Select(_ => NS).ToArray();
        static long Count<T>(this IEnumerable<T> x, Func<T, bool> pred) => Enumerable.Count(x, pred);
        static IEnumerable<T> Repeat<T>(T v, long n) => Enumerable.Repeat<T>(v, (int)n);
        static IEnumerable<int> Range(long s, long c) => Enumerable.Range((int)s, (int)c);
        static IOrderedEnumerable<T> OrderByRand<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x, _ => xorshift);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x) => Enumerable.OrderBy(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderBy<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderBy(x.OrderByRand(), selector);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x) => Enumerable.OrderByDescending(x.OrderByRand(), e => e);
        static IOrderedEnumerable<T1> OrderByDescending<T1, T2>(this IEnumerable<T1> x, Func<T1, T2> selector) => Enumerable.OrderByDescending(x.OrderByRand(), selector);
        static IOrderedEnumerable<string> OrderBy(this IEnumerable<string> x) => x.OrderByRand().OrderBy(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderBy(selector, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<string> OrderByDescending(this IEnumerable<string> x) => x.OrderByRand().OrderByDescending(e => e, StringComparer.OrdinalIgnoreCase);
        static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> x, Func<T, string> selector) => x.OrderByRand().OrderByDescending(selector, StringComparer.OrdinalIgnoreCase);
        static uint xorshift { get { _xsi.MoveNext(); return _xsi.Current; } }
        static IEnumerator<uint> _xsi = _xsc();
        static IEnumerator<uint> _xsc() { uint x = 123456789, y = 362436069, z = 521288629, w = (uint)(DateTime.Now.Ticks & 0xffffffff); while (true) { var t = x ^ (x << 11); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); yield return w; } }
    }
}
namespace Library {
    class LIB_FastIO
    {
        public LIB_FastIO() { str = Console.OpenStandardInput(); }
        readonly Stream str;
        readonly byte[] buf = new byte[1024];
        int len, ptr;
        public bool isEof = false;
        public bool IsEndOfStream { get { return isEof; } }
        byte read()
        {
            if (isEof) throw new EndOfStreamException();
            if (ptr >= len)
            {
                ptr = 0;
                if ((len = str.Read(buf, 0, 1024)) <= 0)
                {
                    isEof = true;
                    return 0;
                }
            }
            return buf[ptr++];
        }
        char Char()
        {
            byte b = 0;
            do b = read();
            while (b < 33 || 126 < b);
            return (char)b;
        }
        virtual public string Scan()
        {
            var sb = new StringBuilder();
            for (var b = Char(); b >= 33 && b <= 126; b = (char)read())
                sb.Append(b);
            return sb.ToString();
        }
        virtual public long Long()
        {
            long ret = 0; byte b = 0; var ng = false;
            do b = read();
            while (b != '-' && (b < '0' || '9' < b));
            if (b == '-') { ng = true; b = read(); }
            for (; true; b = read())
            {
                if (b < '0' || '9' < b)
                    return ng ? -ret : ret;
                else ret = ret * 10 + b - '0';
            }
        }
        virtual public double Double() { return double.Parse(Scan(), CultureInfo.InvariantCulture); }
    }
    class LIB_FastIODebug : LIB_FastIO
    {
        Queue<string> param = new Queue<string>();
        string NextString() { if (param.Count == 0) foreach (var item in Console.ReadLine().Split(' ')) param.Enqueue(item); return param.Dequeue(); }
        public LIB_FastIODebug() { }
        public override string Scan() => NextString();
        public override long Long() => long.Parse(NextString());
        public override double Double() => double.Parse(NextString());
    }
}


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Codeforces.R1364.D
{
    public static class Solver
    {
        public static void Main()
        {
            using (var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false })
            {
                Solve(Console.In, sw);
            }
        }

        public static void Solve(TextReader tr, TextWriter tw)
        {
            WriteAnswer(tw, Parse(tr));
        }

        public struct Answer
        {
            public int Type;
            public long[] Array;
        }

        private static void WriteAnswer(TextWriter tw, Answer answer)
        {

            tw.WriteLine(answer.Type);
            if (answer.Type == 2)
            {
                tw.WriteLine(answer.Array.Length);
            }
            tw.WriteLine(string.Join(" ", answer.Array));
        }

        private static Answer Parse(TextReader tr)
        {
            var w = tr.ReadLine().Split().Select(x => int.Parse(x)).ToArray();
            var uv = new int[w[1]][];
            for (var i = 0; i < uv.Length; ++i)
            {
                uv[i] = tr.ReadLine().Split().Select(y => int.Parse(y)).ToArray();
            }
            return Calc(w[0], w[2], uv);
        }

        public static Answer Calc(int n, long k, int[][] uv)
        {
            var answer = new Answer();

            var v = new List<int>[n + 1];

            foreach (var x in uv)
            {
                if (v[x[0]] == null)
                    v[x[0]] = new List<int>();
                v[x[0]].Add(x[1]);

                if (v[x[1]] == null)
                    v[x[1]] = new List<int>();
                v[x[1]].Add(x[0]);
            }

            var pos = Enumerable.Repeat(-1, n + 1).ToArray();
            var s = new List<long>();
            var col = new List<long>[2];
            col[0] = new List<long>();
            col[1] = new List<long>();
            var cyc = new List<long>();
            Dfs(1, v, pos, s, col, cyc);

            if (!cyc.Any())
            {
                var target = 0;
                if (col[0].Count < col[1].Count)
                {
                    target = 1;
                }

                answer.Type = 1;
                answer.Array = new long[(k + 1) / 2];
                for (var i = 0; i < answer.Array.Length; ++i)
                {
                    answer.Array[i] = col[target][i];
                }
            }
            else
            {
                if (cyc.Count <= k)
                {
                    answer.Type = 2;
                    answer.Array = cyc.ToArray();
                }
                else
                {
                    answer.Type = 1;
                    answer.Array = new long[(k + 1) / 2];
                    for (var i = 0; i < (k+1)/2;++i)
                    {
                        answer.Array[i] = cyc[i * 2];
                    }
                }
            }

            return answer;
        }

        private static bool Dfs(int node, List<int>[] v, int[] pos, List<long> s, List<long>[] col, List<long> cyc)
        {
            pos[node] = s.Count;
            col[pos[node] % 2].Add(node);
            s.Add(node);

            var low = -1;
            foreach (var u in v[node])
            {
                if (pos[u] != -1 && pos[node] - pos[u] > 1)
                    low = Math.Max(low, pos[u]);
            }
            if (low != -1)
            {
                for (int i = low; i <= pos[node]; ++i)
                    cyc.Add(s[i]);
                return true;
            }

            foreach (var u in v[node])
            {
                if (pos[u] == -1)
                {
                    if (Dfs(u, v, pos, s, col, cyc))
                    {
                        return true;
                    }
                }
            }

            s.RemoveAt(s.Count - 1);
            return false;
        }
    }
}


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Codeforces.R1364.D
{
    public static class Solver
    {
        public static void Main()
        {
            using (var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false })
            {
                Solve(Console.In, sw);
            }
        }

        public static void Solve(TextReader tr, TextWriter tw)
        {
            WriteAnswer(tw, Parse(tr));
        }

        public struct Answer
        {
            public int Type;
            public long[] Array;
        }

        private static void WriteAnswer(TextWriter tw, Answer answer)
        {

            tw.WriteLine(answer.Type);
            if (answer.Type == 2)
            {
                tw.WriteLine(answer.Array.Length);
            }
            tw.WriteLine(string.Join(" ", answer.Array));
        }

        private static Answer Parse(TextReader tr)
        {
            var w = tr.ReadLine().Split().Select(x => int.Parse(x)).ToArray();
            var uv = new int[w[1]][];
            for (var i = 0; i < uv.Length; ++i)
            {
                uv[i] = tr.ReadLine().Split().Select(y => int.Parse(y)).ToArray();
            }
            return Calc(w[0], w[2], uv);
        }

        public static Answer Calc(int n, long k, int[][] uv)
        {
            var answer = new Answer();

            var v = new List<int>[n + 1];

            foreach (var x in uv)
            {
                if (v[x[0]] == null)
                    v[x[0]] = new List<int>();
                v[x[0]].Add(x[1]);

                if (v[x[1]] == null)
                    v[x[1]] = new List<int>();
                v[x[1]].Add(x[0]);
            }

            var pos = Enumerable.Repeat(-1, n + 1).ToArray();
            var s = new List<long>();
            var col = new List<long>[2];
            col[0] = new List<long>();
            col[1] = new List<long>();
            var cyc = new List<long>();
            Dfs(1, v, pos, s, col, cyc);

            if (!cyc.Any())
            {
                var target = 0;
                if (col[0].Count < col[1].Count)
                {
                    target = 1;
                }

                answer.Type = 1;
                answer.Array = new long[(k + 1) / 2];
                for (var i = 0; i < answer.Array.Length; ++i)
                {
                    answer.Array[i] = col[target][i];
                }
            }
            else
            {
                if (cyc.Count <= k)
                {
                    answer.Type = 2;
                    answer.Array = cyc.ToArray();
                }
                else
                {
                    answer.Type = 1;
                    answer.Array = new long[(k + 1) / 2];
                    for (var i = 0; i < (k+1)/2;++i)
                    {
                        answer.Array[i] = cyc[i * 2];
                    }
                }
            }

            return answer;
        }

        private static bool Dfs(int node2, List<int>[] v, int[] pos, List<long> s, List<long>[] col, List<long> cyc)
        {
            var stack = new Stack<(int, bool)>();
            stack.Push((node2, false));
            stack.Push((node2, true));

            while (stack.Any())
            {
                var t = stack.Pop();
                var node = t.Item1;
                var b = t.Item2;

                if (!b)
                {
                    s.RemoveAt(s.Count - 1);
                    continue;
                }

                pos[node] = s.Count;
                col[pos[node] % 2].Add(node);
                s.Add(node);

                var low = -1;
                foreach (var u in v[node])
                {
                    if (pos[u] != -1 && pos[node] - pos[u] > 1)
                        low = Math.Max(low, pos[u]);
                }
                if (low != -1)
                {
                    for (int i = low; i <= pos[node]; ++i)
                        cyc.Add(s[i]);
                    return true;
                }

                foreach (var u in v[node])
                {
                    if (pos[u] == -1)
                    {
                        stack.Push((u, false));
                        stack.Push((u, true));
                    }
                }
            }

            return false;
        }
    }
}


use std::fmt;
use std::fmt::Debug;
use std::str::FromStr;

pub struct TokenReader {
    reader: std::io::Stdin,
    tokens: Vec<String>,
    index: usize,
}

impl TokenReader {
    pub fn new() -> Self {
        Self {
            reader: std::io::stdin(),
            tokens: Vec::new(),
            index: 0,
        }
    }

    pub fn next<T>(&mut self) -> T
    where
        T: FromStr,
        T::Err: Debug,
    {
        if self.index >= self.tokens.len() {
            self.load_next_line();
        }
        self.index += 1;
        self.tokens[self.index - 1].parse().unwrap()
    }

    pub fn vector<T>(&mut self) -> Vec<T>
    where
        T: FromStr,
        T::Err: Debug,
    {
        if self.index >= self.tokens.len() {
            self.load_next_line();
        }
        self.index = self.tokens.len();
        self.tokens.iter().map(|tok| tok.parse().unwrap()).collect()
    }

    pub fn load_next_line(&mut self) {
        let mut line = String::new();
        self.reader.read_line(&mut line).unwrap();

        self.tokens = line.split_whitespace().map(String::from).collect();
        self.index = 0;
    }
}

#[derive(Clone)]
struct Node {
    edges: Vec<usize>,
}

impl Node {
    fn new() -> Self {
        Self { edges: vec![] }
    }
}

type Graph = Vec<Node>;

enum Res {
    Set(Vec<usize>),
    Cycle(Vec<usize>),
}

impl fmt::Display for Res {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let nodes = match self {
            Res::Set(nodes) => {
                write!(f, "1\n")?;
                nodes
            }
            Res::Cycle(nodes) => {
                write!(f, "2\n{}\n", nodes.len())?;
                nodes
            }
        };
        for &num in nodes.iter() {
            write!(f, "{} ", num + 1)?;
        }
        Ok(())
    }
}

fn extract_set(stack: &Vec<usize>) -> Vec<usize> {
    (0..stack.len()).step_by(2).map(|i| stack[i]).collect()
}

fn extract_cycle(stack: &Vec<usize>, start: usize) -> Vec<usize> {
    let start = stack.iter().position(|&node| node == start).unwrap();
    stack[start..].to_vec()
}

fn connected(graph: &Graph, a: usize, b: usize) -> bool {
    graph[a].edges.iter().any(|&x| x == b)
}

fn dfs(graph: &Graph, k: usize, stack: &mut Vec<usize>, in_stack: &mut Vec<bool>) -> Option<Res> {
    if stack.len() > k {
        return None;
    }

    let node = stack[stack.len() - 1];
    for &next in graph[node].edges.iter() {
        if stack.len() > 1 && next == stack[stack.len() - 2] {
            continue;
        }

        if in_stack[next] {
            return Some(Res::Cycle(extract_cycle(&stack, next)));
        } else {
            stack.push(next);
            in_stack[next] = true;

            if (stack.len() + 1) / 2 >= (k + 1) / 2 {
                if !connected(&graph, stack[0], stack[stack.len() - 1]) {
                    return Some(Res::Set(extract_set(&stack)));
                }
            }

            let res = dfs(graph, k, stack, in_stack);
            if res.is_some() {
                return res;
            } else {
                stack.pop();
                in_stack[next] = false;
            }
        }
    }
    None
}

fn is_tree(graph: &Graph) -> bool {
    let mut father = vec![None; graph.len()];
    let mut visited = vec![false; graph.len()];

    use std::collections::VecDeque;
    let mut queue = VecDeque::new();

    queue.push_back(0);
    visited[0] = true;

    while let Some(node) = queue.pop_front() {
        for &next in graph[node].edges.iter() {
            if father[node].is_some() && next == father[node].unwrap() {
                continue;
            }
            if visited[next] {
                return false;
            } else {
                father[next] = Some(node);
                visited[next] = true;
                queue.push_back(next);
            }
        }
    }
    visited.iter().all(|&b| b)
}

fn tree_set(graph: &Graph, k: usize) -> Vec<usize> {
    use std::collections::VecDeque;
    let mut queue = VecDeque::new();
    let mut color = vec![0; graph.len()];
    let mut count = vec![1, 0];
    let mut visited = vec![false; graph.len()];

    visited[0] = true;
    color[0] = 1;
    count[color[0]] = 1;
    queue.push_back(0);

    while let Some(node) = queue.pop_front() {
        for &next in graph[node].edges.iter() {
            if !visited[next] {
                visited[next] = true;
                color[next] = 1 - color[node];
                count[color[next]] += 1;
                queue.push_back(next);
            }
        }
    }

    let best = if count[0] > count[1] { 0 } else { 1 };
    (0..graph.len())
        .filter(|&node| color[node] == best)
        .take(k)
        .collect()
}

fn solve(graph: &mut Graph, k: usize) -> Res {
    if is_tree(&graph) {
        return Res::Set(tree_set(&graph, (k + 1) / 2));
    }

    for node in 0..graph.len() {
        graph[node].edges.sort();
    }

    for i in 0..graph.len() {
        let mut stack = vec![i];
        let mut in_stack = vec![false; graph.len()];
        in_stack[i] = true;

        let maybe_res = dfs(&graph, k, &mut stack, &mut in_stack);
        if let Some(res) = maybe_res {
            return res;
        }
    }
    Res::Cycle(vec![])
}

fn main() {
    let mut reader = TokenReader::new();
    let nodes = reader.next();
    let edges = reader.next();
    let k = reader.next();

    let mut graph = vec![Node::new(); nodes];
    for _ in 0..edges {
        let a = reader.next::<usize>();
        let b = reader.next::<usize>();
        graph[a - 1].edges.push(b - 1);
        graph[b - 1].edges.push(a - 1);
    }

    let res = solve(&mut graph, k);
    println!("{}", res);
}


use std::fmt;
use std::fmt::Debug;
use std::str::FromStr;

pub struct TokenReader {
    reader: std::io::Stdin,
    tokens: Vec<String>,
    index: usize,
}

impl TokenReader {
    pub fn new() -> Self {
        Self {
            reader: std::io::stdin(),
            tokens: Vec::new(),
            index: 0,
        }
    }

    pub fn next<T>(&mut self) -> T
    where
        T: FromStr,
        T::Err: Debug,
    {
        if self.index >= self.tokens.len() {
            self.load_next_line();
        }
        self.index += 1;
        self.tokens[self.index - 1].parse().unwrap()
    }

    pub fn vector<T>(&mut self) -> Vec<T>
    where
        T: FromStr,
        T::Err: Debug,
    {
        if self.index >= self.tokens.len() {
            self.load_next_line();
        }
        self.index = self.tokens.len();
        self.tokens.iter().map(|tok| tok.parse().unwrap()).collect()
    }

    pub fn load_next_line(&mut self) {
        let mut line = String::new();
        self.reader.read_line(&mut line).unwrap();

        self.tokens = line.split_whitespace().map(String::from).collect();
        self.index = 0;
    }
}

#[derive(Clone)]
struct Node {
    edges: Vec<usize>,
}

impl Node {
    fn new() -> Self {
        Self { edges: vec![] }
    }
}

type Graph = Vec<Node>;

enum Res {
    Set(Vec<usize>),
    Cycle(Vec<usize>),
}

impl fmt::Display for Res {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let nodes = match self {
            Res::Set(nodes) => {
                write!(f, "1\n")?;
                nodes
            }
            Res::Cycle(nodes) => {
                write!(f, "2\n{}\n", nodes.len())?;
                nodes
            }
        };
        for &num in nodes.iter() {
            write!(f, "{} ", num + 1)?;
        }
        Ok(())
    }
}

fn extract_set(stack: &Vec<usize>) -> Vec<usize> {
    (0..stack.len()).step_by(2).map(|i| stack[i]).collect()
}

fn extract_cycle(stack: &Vec<usize>, start: usize) -> Vec<usize> {
    let start = stack.iter().position(|&node| node == start).unwrap();
    stack[start..].to_vec()
}

fn connected(graph: &Graph, a: usize, b: usize) -> bool {
    graph[a].edges.iter().any(|&x| x == b)
}

fn dfs(graph: &Graph, k: usize, stack: &mut Vec<usize>, in_stack: &mut Vec<bool>) -> Option<Res> {
    if stack.len() > k {
        return None;
    }

    let node = stack[stack.len() - 1];
    for &next in graph[node].edges.iter() {
        if stack.len() > 1 && next == stack[stack.len() - 2] {
            continue;
        }
        if in_stack[next] {
            return Some(Res::Cycle(extract_cycle(&stack, next)));
        }
    }

    if (stack.len() + 1) / 2 >= (k + 1) / 2 {
        return Some(Res::Set(extract_set(&stack)));
    }

    for &next in graph[node].edges.iter() {
        if stack.len() > 1 && next == stack[stack.len() - 2] {
            continue;
        }

        stack.push(next);
        in_stack[next] = true;
        if let Some(res) = dfs(graph, k, stack, in_stack) {
            return Some(res);
        }
        stack.pop();
        in_stack[next] = false;
    }
    None
}

fn is_tree(graph: &Graph) -> bool {
    let mut father = vec![None; graph.len()];
    let mut visited = vec![false; graph.len()];

    use std::collections::VecDeque;
    let mut queue = VecDeque::new();

    queue.push_back(0);
    visited[0] = true;

    while let Some(node) = queue.pop_front() {
        for &next in graph[node].edges.iter() {
            if father[node].is_some() && next == father[node].unwrap() {
                continue;
            }
            if visited[next] {
                return false;
            } else {
                father[next] = Some(node);
                visited[next] = true;
                queue.push_back(next);
            }
        }
    }
    visited.iter().all(|&b| b)
}

fn tree_set(graph: &Graph, k: usize) -> Vec<usize> {
    use std::collections::VecDeque;
    let mut queue = VecDeque::new();
    let mut color = vec![0; graph.len()];
    let mut count = vec![1, 0];
    let mut visited = vec![false; graph.len()];

    visited[0] = true;
    color[0] = 1;
    count[color[0]] = 1;
    queue.push_back(0);

    while let Some(node) = queue.pop_front() {
        for &next in graph[node].edges.iter() {
            if !visited[next] {
                visited[next] = true;
                color[next] = 1 - color[node];
                count[color[next]] += 1;
                queue.push_back(next);
            }
        }
    }

    let best = if count[0] > count[1] { 0 } else { 1 };
    (0..graph.len())
        .filter(|&node| color[node] == best)
        .take(k)
        .collect()
}

fn solve(graph: &mut Graph, k: usize) -> Res {
    if is_tree(&graph) {
        return Res::Set(tree_set(&graph, (k + 1) / 2));
    }

    for node in 0..graph.len() {
        graph[node].edges.sort();
    }

    for i in 0..graph.len() {
        let mut stack = vec![i];
        let mut in_stack = vec![false; graph.len()];
        in_stack[i] = true;

        let maybe_res = dfs(&graph, k, &mut stack, &mut in_stack);
        if let Some(res) = maybe_res {
            return res;
        }
    }
    Res::Cycle(vec![])
}

fn main() {
    let mut reader = TokenReader::new();
    let nodes = reader.next();
    let edges = reader.next();
    let k = reader.next();

    let mut graph = vec![Node::new(); nodes];
    for _ in 0..edges {
        let a = reader.next::<usize>();
        let b = reader.next::<usize>();
        graph[a - 1].edges.push(b - 1);
        graph[b - 1].edges.push(a - 1);
    }

    let res = solve(&mut graph, k);
    println!("{}", res);
}


use std::fmt;
use std::fmt::Debug;
use std::str::FromStr;

pub struct TokenReader {
    reader: std::io::Stdin,
    tokens: Vec<String>,
    index: usize,
}

impl TokenReader {
    pub fn new() -> Self {
        Self {
            reader: std::io::stdin(),
            tokens: Vec::new(),
            index: 0,
        }
    }

    pub fn next<T>(&mut self) -> T
    where
        T: FromStr,
        T::Err: Debug,
    {
        if self.index >= self.tokens.len() {
            self.load_next_line();
        }
        self.index += 1;
        self.tokens[self.index - 1].parse().unwrap()
    }

    pub fn vector<T>(&mut self) -> Vec<T>
    where
        T: FromStr,
        T::Err: Debug,
    {
        if self.index >= self.tokens.len() {
            self.load_next_line();
        }
        self.index = self.tokens.len();
        self.tokens.iter().map(|tok| tok.parse().unwrap()).collect()
    }

    pub fn load_next_line(&mut self) {
        let mut line = String::new();
        self.reader.read_line(&mut line).unwrap();

        self.tokens = line.split_whitespace().map(String::from).collect();
        self.index = 0;
    }
}

#[derive(Clone)]
struct Node {
    edges: Vec<usize>,
}

impl Node {
    fn new() -> Self {
        Self { edges: vec![] }
    }
}

type Graph = Vec<Node>;

enum Res {
    Set(Vec<usize>),
    Cycle(Vec<usize>),
}

impl fmt::Display for Res {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let nodes = match self {
            Res::Set(nodes) => {
                write!(f, "1\n")?;
                nodes
            }
            Res::Cycle(nodes) => {
                write!(f, "2\n{}\n", nodes.len())?;
                nodes
            }
        };
        for &num in nodes.iter() {
            write!(f, "{} ", num + 1)?;
        }
        Ok(())
    }
}

fn extract_set(stack: &Vec<usize>) -> Vec<usize> {
    (0..stack.len()).step_by(2).map(|i| stack[i]).collect()
}

fn extract_cycle(stack: &Vec<usize>, start: usize) -> Vec<usize> {
    let start = stack.iter().position(|&node| node == start).unwrap();
    stack[start..].to_vec()
}

fn connected(graph: &Graph, a: usize, b: usize) -> bool {
    graph[a].edges.iter().any(|&x| x == b)
}

fn dfs(graph: &Graph, k: usize, stack: &mut Vec<usize>, in_stack: &mut Vec<bool>) -> Option<Res> {
    if stack.len() > k {
        return None;
    }

    let node = stack[stack.len() - 1];
    for &next in graph[node].edges.iter() {
        if stack.len() > 1 && next == stack[stack.len() - 2] {
            continue;
        }

        if in_stack[next] {
            return Some(Res::Cycle(extract_cycle(&stack, next)));
        } else {
            stack.push(next);
            in_stack[next] = true;

            if (stack.len() + 1) / 2 >= (k + 1) / 2 {
                if !connected(&graph, stack[0], stack[stack.len() - 1]) {
                    return Some(Res::Set(extract_set(&stack)));
                }
            }

            let res = dfs(graph, k, stack, in_stack);
            if res.is_some() {
                return res;
            } else {
                stack.pop();
                in_stack[next] = false;
            }
        }
    }
    None
}

fn is_tree(graph: &Graph) -> bool {
    let mut father = vec![None; graph.len()];
    let mut visited = vec![false; graph.len()];

    use std::collections::VecDeque;
    let mut queue = VecDeque::new();

    queue.push_back(0);
    visited[0] = true;

    while let Some(node) = queue.pop_front() {
        for &next in graph[node].edges.iter() {
            if father[node].is_some() && next == father[node].unwrap() {
                continue;
            }
            if visited[next] {
                return false;
            } else {
                father[next] = Some(node);
                visited[next] = true;
                queue.push_back(next);
            }
        }
    }
    visited.iter().all(|&b| b)
}

fn tree_set(graph: &Graph, k: usize) -> Vec<usize> {
    use std::collections::VecDeque;
    let mut queue = VecDeque::new();
    let mut color = vec![0; graph.len()];
    let mut count = vec![1, 0];
    let mut visited = vec![false; graph.len()];

    visited[0] = true;
    color[0] = 1;
    count[color[0]] = 1;
    queue.push_back(0);

    while let Some(node) = queue.pop_front() {
        for &next in graph[node].edges.iter() {
            if !visited[next] {
                visited[next] = true;
                color[next] = 1 - color[node];
                count[color[next]] += 1;
                queue.push_back(next);
            }
        }
    }

    let best = if count[0] > count[1] { 0 } else { 1 };
    (0..graph.len())
        .filter(|&node| color[node] == best)
        .take(k)
        .collect()
}

fn solve(graph: &Graph, k: usize) -> Res {
    if is_tree(&graph) {
        return Res::Set(tree_set(&graph, (k + 1) / 2));
    }

    for i in 0..graph.len() {
        let mut stack = vec![i];
        let mut in_stack = vec![false; graph.len()];
        in_stack[i] = true;

        let maybe_res = dfs(&graph, k, &mut stack, &mut in_stack);
        if let Some(res) = maybe_res {
            return res;
        }
    }
    Res::Cycle(vec![])
}

fn main() {
    let mut reader = TokenReader::new();
    let nodes = reader.next();
    let edges = reader.next();
    let k = reader.next();

    let mut graph = vec![Node::new(); nodes];
    for _ in 0..edges {
        let a = reader.next::<usize>();
        let b = reader.next::<usize>();
        graph[a - 1].edges.push(b - 1);
        graph[b - 1].edges.push(a - 1);
    }

    let res = solve(&graph, k);
    println!("{}", res);
}