```go
package main
import (
"bufio"
"fmt"
"os"
)
type FastScanner struct {
r *bufio.Reader
}
func NewFastScanner() *FastScanner {
return &FastScanner{r: bufio.NewReaderSize(os.Stdin, 1<<20)}
}
func (fs *FastScanner) NextInt() int {
sign, val := 1, 0
c, err := fs.r.ReadByte()
for (c < '0' || c > '9') && c != '-' {
c, err = fs.r.ReadByte()
if err != nil {
return 0
}
}
if c == '-' {
sign = -1
c, _ = fs.r.ReadByte()
}
for c >= '0' && c <= '9' {
val = val*10 + int(c-'0')
c, err = fs.r.ReadByte()
if err != nil {
break
}
}
if err == nil && (c < '0' || c > '9') && c != '-' {
fs.r.UnreadByte()
}
return sign * val
}
type DSU struct {
parent []int
size []int
mx []int
}
func NewDSU(n int) *DSU {
parent := make([]int, n)
size := make([]int, n)
mx := make([]int, n)
for i := 0; i < n; i++ {
parent[i] = i
size[i] = 1
mx[i] = i
}
return &DSU{parent: parent, size: size, mx: mx}
}
func (d *DSU) Find(x int) int {
for d.parent[x] != x {
d.parent[x] = d.parent[d.parent[x]]
x = d.parent[x]
}
return x
}
func (d *DSU) Union(a, b int) int {
ra := d.Find(a)
rb := d.Find(b)
if ra == rb {
return ra
}
if d.size[ra] < d.size[rb] {
ra, rb = rb, ra
}
d.parent[rb] = ra
d.size[ra] += d.size[rb]
if d.mx[rb] > d.mx[ra] {
d.mx[ra] = d.mx[rb]
}
return ra
}
func main() {
fs := NewFastScanner()
n := fs.NextInt()
m := fs.NextInt()
dsu := NewDSU(n)
for i := 0; i < m; i++ {
u := fs.NextInt() - 1
v := fs.NextInt() - 1
dsu.Union(u, v)
}
ans := 0
for i := 0; i < n; {
root := dsu.Find(i)
r := dsu.mx[root]
for j := i + 1; j <= r; j++ {
rj := dsu.Find(j)
if rj != root {
ans++
root = dsu.Union(root, rj)
if dsu.mx[root] > r {
r = dsu.mx[root]
}
}
}
i = r + 1
}
out := bufio.NewWriterSize(os.Stdout, 1<<20)
fmt.Fprintln(out, ans)
out.Flush()
}
```