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)
}