← Home
package main

import (
	"fmt"
	"io"
	"os"
)

type Node struct {
	ch       [2]*Node
	fa       *Node
	covTag   int
	cover    int
	maxCover int
	rev      bool
	is_edge  bool
	has_edge bool
}

func is_root(x *Node) bool {
	return x.fa == nil || (x.fa.ch[0] != x && x.fa.ch[1] != x)
}

func update(x *Node) {
	x.maxCover = 0
	if x.is_edge {
		x.maxCover = x.cover
	}
	if x.ch[0] != nil && x.ch[0].maxCover > x.maxCover {
		x.maxCover = x.ch[0].maxCover
	}
	if x.ch[1] != nil && x.ch[1].maxCover > x.maxCover {
		x.maxCover = x.ch[1].maxCover
	}

	x.has_edge = x.is_edge
	if x.ch[0] != nil && x.ch[0].has_edge {
		x.has_edge = true
	}
	if x.ch[1] != nil && x.ch[1].has_edge {
		x.has_edge = true
	}
}

func apply_cov(x *Node, val int) {
	if x == nil {
		return
	}
	x.covTag = val
	if x.is_edge {
		x.cover = val
	}
	if x.has_edge {
		x.maxCover = val
	}
}

func push_down(x *Node) {
	if x.rev {
		x.ch[0], x.ch[1] = x.ch[1], x.ch[0]
		if x.ch[0] != nil {
			x.ch[0].rev = !x.ch[0].rev
		}
		if x.ch[1] != nil {
			x.ch[1].rev = !x.ch[1].rev
		}
		x.rev = false
	}
	if x.covTag != -1 {
		if x.ch[0] != nil {
			apply_cov(x.ch[0], x.covTag)
		}
		if x.ch[1] != nil {
			apply_cov(x.ch[1], x.covTag)
		}
		x.covTag = -1
	}
}

func rotate(x *Node) {
	y := x.fa
	z := y.fa
	k := 0
	if y.ch[1] == x {
		k = 1
	}
	if !is_root(y) {
		if z.ch[0] == y {
			z.ch[0] = x
		} else {
			z.ch[1] = x
		}
	}
	x.fa = z
	y.ch[k] = x.ch[k^1]
	if x.ch[k^1] != nil {
		x.ch[k^1].fa = y
	}
	x.ch[k^1] = y
	y.fa = x
	update(y)
	update(x)
}

var splayStack = make([]*Node, 0, 1024)

func push_all(x *Node) {
	splayStack = splayStack[:0]
	curr := x
	for {
		splayStack = append(splayStack, curr)
		if is_root(curr) {
			break
		}
		curr = curr.fa
	}
	for i := len(splayStack) - 1; i >= 0; i-- {
		push_down(splayStack[i])
	}
}

func splay(x *Node) {
	push_all(x)
	for !is_root(x) {
		y := x.fa
		z := y.fa
		if !is_root(y) {
			yIsRight := y == z.ch[1]
			xIsRight := x == y.ch[1]
			if yIsRight == xIsRight {
				rotate(y)
			} else {
				rotate(x)
			}
		}
		rotate(x)
	}
}

func access(x *Node) {
	for y := (*Node)(nil); x != nil; y, x = x, x.fa {
		splay(x)
		x.ch[1] = y
		update(x)
	}
}

func make_root(x *Node) {
	access(x)
	splay(x)
	x.rev = !x.rev
}

func find_root(x *Node) *Node {
	access(x)
	splay(x)
	for x.ch[0] != nil {
		push_down(x)
		x = x.ch[0]
	}
	splay(x)
	return x
}

func connected(x, y *Node) bool {
	if x == y {
		return true
	}
	make_root(x)
	return find_root(y) == x
}

func link(x, y *Node) {
	make_root(x)
	if find_root(y) != x {
		x.fa = y
	}
}

func cut(x, y *Node) {
	make_root(x)
	access(y)
	splay(y)
	if y.ch[0] == x && x.fa == y && x.ch[1] == nil {
		y.ch[0] = nil
		x.fa = nil
		update(y)
	}
}

var (
	nodes []Node
	U     []int
	V     []int
	state []int
	N     int
	M     int
)

const (
	DELETED  = 0
	TREE     = 1
	NON_TREE = 2
)

func cut_edge(id int) {
	E := &nodes[N+id]
	u := &nodes[U[id]]
	v := &nodes[V[id]]
	cut(u, E)
	cut(v, E)
}

func link_edge(id int) {
	E := &nodes[N+id]
	u := &nodes[U[id]]
	v := &nodes[V[id]]
	link(u, E)
	link(v, E)
}

func get_max_cover(u_id, v_id int) int {
	u := &nodes[u_id]
	v := &nodes[v_id]
	make_root(u)
	access(v)
	splay(v)
	return v.maxCover
}

func set_cover_path(u_id, v_id, val int) {
	u := &nodes[u_id]
	v := &nodes[v_id]
	make_root(u)
	access(v)
	splay(v)
	apply_cov(v, val)
}

func delete_edge(id int) {
	if state[id] == TREE {
		E := &nodes[N+id]
		splay(E)
		c := E.cover
		if c > 0 {
			set_cover_path(U[c], V[c], 0)
			cut_edge(id)
			link_edge(c)
			state[id] = DELETED
			state[c] = TREE
		} else {
			cut_edge(id)
			state[id] = DELETED
		}
	} else if state[id] == NON_TREE {
		set_cover_path(U[id], V[id], 0)
		state[id] = DELETED
	}
}

func main() {
	bytes, _ := io.ReadAll(os.Stdin)
	var cursor int
	nextInt := func() int {
		for cursor < len(bytes) && bytes[cursor] <= ' ' {
			cursor++
		}
		if cursor >= len(bytes) {
			return 0
		}
		res := 0
		for cursor < len(bytes) && bytes[cursor] > ' ' {
			res = res*10 + int(bytes[cursor]-'0')
			cursor++
		}
		return res
	}

	N = nextInt()
	M = nextInt()

	if N == 0 && M == 0 {
		return
	}

	nodes = make([]Node, N+M+1)
	for i := 1; i <= N+M; i++ {
		nodes[i].covTag = -1
		if i > N {
			nodes[i].is_edge = true
			nodes[i].has_edge = true
		}
	}

	U = make([]int, M+1)
	V = make([]int, M+1)
	state = make([]int, M+1)

	for i := 1; i <= M; i++ {
		U[i] = nextInt()
		V[i] = nextInt()
	}

	L := 1
	ans := int64(0)

	for R := 1; R <= M; R++ {
		u := U[R]
		v := V[R]

		unode := &nodes[u]
		vnode := &nodes[v]

		for connected(unode, vnode) && get_max_cover(u, v) > 0 {
			delete_edge(L)
			L++
		}

		if connected(unode, vnode) {
			state[R] = NON_TREE
			set_cover_path(u, v, R)
		} else {
			state[R] = TREE
			link_edge(R)
		}
		ans += int64(R - L + 1)
	}

	fmt.Println(ans)
}