package main
import (
"os"
)
type Edge struct {
u, v int32
}
type EdgeList struct {
e Edge
next int32
}
const (
AddA = 1
AddAUB = 2
AddBoth = 3
)
var pool []EdgeList
var headA []int32
var headAUB []int32
func addEdgeNode(node int, e Edge, tpe int) {
if tpe&AddA != 0 {
idx := int32(len(pool))
pool = append(pool, EdgeList{e, headA[node]})
headA[node] = idx
}
if tpe&AddAUB != 0 {
idx := int32(len(pool))
pool = append(pool, EdgeList{e, headAUB[node]})
headAUB[node] = idx
}
}
func addEdge(node, L, R, l, r int, e Edge, tpe int) {
if l <= L && R <= r {
addEdgeNode(node, e, tpe)
return
}
mid := L + (R - L)/2
if l <= mid {
addEdge(node*2, L, mid, l, r, e, tpe)
}
if r > mid {
addEdge(node*2+1, mid+1, R, l, r, e, tpe)
}
}
type DSUState struct {
u int32
v int32
}
type DSU struct {
parent []int32
size []int32
comps int32
history []DSUState
}
func NewDSU(n int32) *DSU {
d := &DSU{
parent: make([]int32, n+1),
size: make([]int32, n+1),
comps: n,
history: make([]DSUState, 0, n),
}
for i := int32(1); i <= n; i++ {
d.parent[i] = i
d.size[i] = 1
}
return d
}
func (d *DSU) Find(i int32) int32 {
for d.parent[i] != i {
i = d.parent[i]
}
return i
}
func (d *DSU) Merge(u, v int32) {
rootU := d.Find(u)
rootV := d.Find(v)
if rootU != rootV {
if d.size[rootU] < d.size[rootV] {
rootU, rootV = rootV, rootU
}
d.parent[rootV] = rootU
d.size[rootU] += d.size[rootV]
d.comps--
d.history = append(d.history, DSUState{rootU, rootV})
}
}
func (d *DSU) Rollback(target int) {
for len(d.history) > target {
state := d.history[len(d.history)-1]
d.history = d.history[:len(d.history)-1]
d.parent[state.v] = state.v
d.size[state.u] -= d.size[state.v]
d.comps++
}
}
func main() {
buf, _ := os.ReadFile("/dev/stdin")
pos := 0
nextInt := func() int32 {
for pos < len(buf) && buf[pos] <= ' ' {
pos++
}
if pos >= len(buf) {
return 0
}
res := int32(0)
for pos < len(buf) && buf[pos] > ' ' {
res = res*10 + int32(buf[pos]-'0')
pos++
}
return res
}
nextChar := func() byte {
for pos < len(buf) && buf[pos] <= ' ' {
pos++
}
if pos >= len(buf) {
return 0
}
res := buf[pos]
for pos < len(buf) && buf[pos] > ' ' {
pos++
}
return res
}
n := nextInt()
q := nextInt()
if q == 0 {
return
}
headA = make([]int32, 4*q+1)
headAUB = make([]int32, 4*q+1)
for i := range headA {
headA[i] = -1
headAUB[i] = -1
}
pool = make([]EdgeList, 0, 32000000)
mapA := make(map[Edge]int)
mapB := make(map[Edge]int)
for i := 0; i < int(q); i++ {
c := nextChar()
u := nextInt()
v := nextInt()
if u > v {
u, v = v, u
}
e := Edge{u, v}
if c == 'A' {
if start, ok := mapA[e]; ok {
addEdge(1, 0, int(q)-1, start, i-1, e, AddBoth)
delete(mapA, e)
} else {
mapA[e] = i
}
} else if c == 'B' {
if start, ok := mapB[e]; ok {
addEdge(1, 0, int(q)-1, start, i-1, e, AddAUB)
delete(mapB, e)
} else {
mapB[e] = i
}
}
}
for e, start := range mapA {
addEdge(1, 0, int(q)-1, start, int(q)-1, e, AddBoth)
}
for e, start := range mapB {
addEdge(1, 0, int(q)-1, start, int(q)-1, e, AddAUB)
}
dsuA := NewDSU(n)
dsuAUB := NewDSU(n)
ans := make([]int32, q)
var solve func(node, L, R int)
solve = func(node, L, R int) {
histA := len(dsuA.history)
histAUB := len(dsuAUB.history)
for idx := headA[node]; idx != -1; idx = pool[idx].next {
dsuA.Merge(pool[idx].e.u, pool[idx].e.v)
}
for idx := headAUB[node]; idx != -1; idx = pool[idx].next {
dsuAUB.Merge(pool[idx].e.u, pool[idx].e.v)
}
if L == R {
ans[L] = dsuA.comps - dsuAUB.comps
} else {
mid := L + (R - L)/2
solve(node*2, L, mid)
solve(node*2+1, mid+1, R)
}
dsuA.Rollback(histA)
dsuAUB.Rollback(histAUB)
}
solve(1, 0, int(q)-1)
out := make([]byte, 0, q*10)
for _, v := range ans {
if v == 0 {
out = append(out, '0')
} else {
var b [12]byte
idx := 12
for v > 0 {
idx--
b[idx] = byte(v%10 + '0')
v /= 10
}
out = append(out, b[idx:]...)
}
out = append(out, '\n')
}
os.Stdout.Write(out)
}