package main
import (
"fmt"
"math/bits"
)
func main() {
var n, m, k int
if _, err := fmt.Scan(&n, &m, &k); err != nil {
return
}
var adj [12][12]bool
for i := 0; i < m; i++ {
var u, v int
fmt.Scan(&u, &v)
u--
v--
adj[u][v] = true
adj[v][u] = true
}
K := n / 2
type Path struct {
seq [6]int
}
var paths [4096][12][]Path
var genPaths func(u int, mask int, depth int, currentSeq *[6]int)
genPaths = func(u int, mask int, depth int, currentSeq *[6]int) {
if depth == K {
paths[mask][u] = append(paths[mask][u], Path{*currentSeq})
return
}
for v := 0; v < n; v++ {
if (mask&(1<<v)) == 0 && adj[u][v] {
currentSeq[depth] = v
genPaths(v, mask|(1<<v), depth+1, currentSeq)
}
}
}
for i := 0; i < n; i++ {
var seq [6]int
seq[0] = i
genPaths(i, 1<<i, 1, &seq)
}
goodMatchings := make(map[uint64]bool)
fullMask := (1 << n) - 1
for mask1 := 0; mask1 < (1<<(n-1)); mask1++ {
if bits.OnesCount(uint(mask1)) != K {
continue
}
mask2 := fullMask ^ mask1
for u1 := 0; u1 < n; u1++ {
if len(paths[mask1][u1]) == 0 {
continue
}
for u2 := 0; u2 < n; u2++ {
if !adj[u1][u2] || len(paths[mask2][u2]) == 0 {
continue
}
for _, p1 := range paths[mask1][u1] {
for _, p2 := range paths[mask2][u2] {
var edges [6]uint64
for i := 0; i < K; i++ {
u, v := p1.seq[i], p2.seq[i]
if u > v {
u, v = v, u
}
edges[i] = uint64((u << 4) | v)
}
for i := 1; i < K; i++ {
for j := i; j > 0 && edges[j-1] > edges[j]; j-- {
edges[j-1], edges[j] = edges[j], edges[j-1]
}
}
var id uint64
for i := 0; i < K; i++ {
id = (id << 8) | edges[i]
}
goodMatchings[id] = true
}
}
}
}
}
goodPartitions := make(map[uint64]bool)
for id := range goodMatchings {
var mate [12]int
for i := 0; i < K; i++ {
edge := (id >> (8 * (K - 1 - i))) & 0xFF
u, v := int(edge>>4), int(edge&0xF)
mate[u] = v
mate[v] = u
}
var rgs uint64
var nextID uint64 = 0
var blockID [12]int
for i := 0; i < 12; i++ {
blockID[i] = -1
}
for i := 0; i < n; i++ {
if blockID[i] == -1 {
blockID[i] = int(nextID)
blockID[mate[i]] = int(nextID)
nextID++
}
rgs |= uint64(blockID[i]) << (4 * i)
}
goodPartitions[rgs] = true
}
queue := make([]uint64, 0, len(goodPartitions))
for rgs := range goodPartitions {
queue = append(queue, rgs)
}
for head := 0; head < len(queue); head++ {
curr := queue[head]
var maxBlock uint64 = 0
var blocks [12]uint64
for i := 0; i < n; i++ {
b := (curr >> (4 * i)) & 0xF
blocks[i] = b
if b > maxBlock {
maxBlock = b
}
}
numBlocks := int(maxBlock + 1)
for b1 := 0; b1 < numBlocks; b1++ {
for b2 := b1 + 1; b2 < numBlocks; b2++ {
var nextRGS uint64
for i := 0; i < n; i++ {
b := blocks[i]
if b == uint64(b2) {
b = uint64(b1)
} else if b > uint64(b2) {
b--
}
nextRGS |= b << (4 * i)
}
if !goodPartitions[nextRGS] {
goodPartitions[nextRGS] = true
queue = append(queue, nextRGS)
}
}
}
}
var ans int64 = 0
for rgs := range goodPartitions {
var maxBlock uint64 = 0
for i := 0; i < n; i++ {
b := (rgs >> (4 * i)) & 0xF
if b > maxBlock {
maxBlock = b
}
}
c := int(maxBlock + 1)
if k >= c {
ways := int64(1)
for i := 0; i < c; i++ {
ways *= int64(k - i)
}
ans += ways
}
}
fmt.Println(ans)
}