```go
package main
import (
"bufio"
"io"
"os"
)
type FastScanner struct {
data []byte
idx int
n int
}
func NewFastScanner() *FastScanner {
data, _ := io.ReadAll(os.Stdin)
return &FastScanner{data: data, n: len(data)}
}
func (fs *FastScanner) skip() {
for fs.idx < fs.n && fs.data[fs.idx] <= ' ' {
fs.idx++
}
}
func (fs *FastScanner) NextByte() byte {
fs.skip()
b := fs.data[fs.idx]
fs.idx++
return b
}
func (fs *FastScanner) NextInt() int {
fs.skip()
x := 0
for fs.idx < fs.n {
b := fs.data[fs.idx]
if b < '0' || b > '9' {
break
}
x = x*10 + int(b-'0')
fs.idx++
}
return x
}
func key(u, v int) uint64 {
return uint64(uint32(u))<<32 | uint64(uint32(v))
}
func main() {
fs := NewFastScanner()
fs.NextInt()
m := fs.NextInt()
edges := make(map[uint64]byte, m*2)
mutual := 0
same := 0
out := bufio.NewWriterSize(os.Stdout, 1<<20)
for i := 0; i < m; i++ {
t := fs.NextByte()
if t == '+' {
u := fs.NextInt()
v := fs.NextInt()
c := fs.NextByte()
if rc, ok := edges[key(v, u)]; ok {
mutual++
if rc == c {
same++
}
}
edges[key(u, v)] = c
} else if t == '-' {
u := fs.NextInt()
v := fs.NextInt()
k := key(u, v)
c := edges[k]
if rc, ok := edges[key(v, u)]; ok {
mutual--
if rc == c {
same--
}
}
delete(edges, k)
} else {
k := fs.NextInt()
if k&1 == 1 {
if mutual > 0 {
out.WriteString("YES\n")
} else {
out.WriteString("NO\n")
}
} else {
if same > 0 {
out.WriteString("YES\n")
} else {
out.WriteString("NO\n")
}
}
}
}
out.Flush()
}
```