```go
package main
import (
"bufio"
"fmt"
"os"
)
const MOD = 998244353
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var n, m, k int
// Read n, m, k
if _, err := fmt.Fscan(in, &n, &m, &k); err != nil {
return
}
// Precompute powers of 2
limit := n
if m > limit {
limit = m
}
pow2 := make([]int64, limit+1)
pow2[0] = 1
for i := 1; i <= limit; i++ {
pow2[i] = (pow2[i-1] * 2) % MOD
}
// Row state tracking
// rowBad0[i]: number of cells in row i that violate pattern P0 (where A[i][j] = (i+j)%2)
// rowBad1[i]: number of cells in row i that violate pattern P1 (where A[i][j] = 1 - (i+j)%2)
rowBad0 := make([]int, n+1)
rowBad1 := make([]int, n+1)
nA_free := n // Number of rows consistent with both patterns (no constraints or empty)
nA_imp := 0 // Number of rows consistent with neither pattern (impossible)
// Column state tracking
// colBad0[j]: number of cells in col j that violate pattern Q0 (where A[i][j] = (i+j)%2)
// colBad1[j]: number of cells in col j that violate pattern Q1 (where A[i][j] = 1 - (i+j)%2)
colBad0 := make([]int, m+1)
colBad1 := make([]int, m+1)
nB_free := m
nB_imp := 0
// Intersection states (specific checkerboard patterns M1 and M2)
// M1 corresponds to A[i][j] = (i+j)%2 everywhere.
// M2 corresponds to A[i][j] = 1 - (i+j)%2 everywhere.
badM1 := 0
badM2 := 0
// Grid to store current values: Key is encoded coordinate
grid := make(map[int64]int)
updateRow := func(r, val, delta, y int) {
target := (r + y) % 2
wasFree := (rowBad0[r] == 0 && rowBad1[r] == 0)
wasImp := (rowBad0[r] > 0 && rowBad1[r] > 0)
if val != target {
rowBad0[r] += delta
} else {
rowBad1[r] += delta
}
isFree := (rowBad0[r] == 0 && rowBad1[r] == 0)
isImp := (rowBad0[r] > 0 && rowBad1[r] > 0)
if wasFree && !isFree {
nA_free--
}
if !wasFree && isFree {
nA_free++
}
if wasImp && !isImp {
nA_imp--
}
if !wasImp && isImp {
nA_imp++
}
}
updateCol := func(c, val, delta, x int) {
target := (x + c) % 2
wasFree := (colBad0[c] == 0 && colBad1[c] == 0)
wasImp := (colBad0[c] > 0 && colBad1[c] > 0)
if val != target {
colBad0[c] += delta
} else {
colBad1[c] += delta
}
isFree := (colBad0[c] == 0 && colBad1[c] == 0)
isImp := (colBad0[c] > 0 && colBad1[c] > 0)
if wasFree && !isFree {
nB_free--
}
if !wasFree && isFree {
nB_free++
}
if wasImp && !isImp {
nB_imp--
}
if !wasImp && isImp {
nB_imp++
}
}
for i := 0; i < k; i++ {
var x, y, t int
fmt.Fscan(in, &x, &y, &t)
key := (int64(x) << 32) | int64(y)
// If cell already has a value, remove it first
if val, ok := grid[key]; ok {
updateRow(x, val, -1, y)
updateCol(y, val, -1, x)
if val != (x+y)%2 {
badM1--
} else {
badM2--
}
delete(grid, key)
}
// If new operation writes a value (0 or 1)
if t != -1 {
grid[key] = t
updateRow(x, t, 1, y)
updateCol(y, t, 1, x)
if t != (x+y)%2 {
badM1++
} else {
badM2++
}
}
// Calculate answer using Principle of Inclusion-Exclusion
ans := int64(0)
// Ways satisfying Case A (Row condition)
if nA_imp == 0 {
ans = (ans + pow2[nA_free]) % MOD
}
// Ways satisfying Case B (Column condition)
if nB_imp == 0 {
ans = (ans + pow2[nB_free]) % MOD
}
// Subtract Intersection (M1 and M2 are the only valid intersections)
if badM1 == 0 {
ans = (ans - 1 + MOD) % MOD
}
if badM2 == 0 {
ans = (ans - 1 + MOD) % MOD
}
fmt.Fprintln(out, ans)
}
}
```