```go
package main
import (
"fmt"
"math/bits"
)
var (
n, m int
maxPieces int = -1
bestBoard [81]byte
mMask uint16
shapes = [4][5][2]int{
{{0, 0}, {0, 1}, {0, 2}, {1, 1}, {2, 1}},
{{0, 0}, {1, -2}, {1, -1}, {1, 0}, {2, 0}},
{{0, 0}, {1, 0}, {2, -1}, {2, 0}, {2, 1}},
{{0, 0}, {1, 0}, {1, 1}, {1, 2}, {2, 0}},
}
)
type Placement struct {
mask [9]uint16
cells [5]int
}
var placements [81][]Placement
var currentPlacements [81]Placement
func initPlacements() {
for c := 0; c < n*m; c++ {
r := c / m
col := c % m
for _, shape := range shapes {
valid := true
var p Placement
for i, offset := range shape {
nr, nc := r+offset[0], col+offset[1]
if nr < 0 || nr >= n || nc < 0 || nc >= m {
valid = false
break
}
p.mask[nr] |= 1 << nc
p.cells[i] = nr*m + nc
}
if valid {
placements[c] = append(placements[c], p)
}
}
}
}
func solve(c int, pieces int, board [9]uint16) {
for c < n*m {
r := c / m
col := c % m
if (board[r] & (1 << col)) == 0 {
break
}
c++
}
if c == n*m {
if pieces > maxPieces {
maxPieces = pieces
for i := 0; i < n*m; i++ {
bestBoard[i] = '.'
}
for i := 0; i < pieces; i++ {
for _, idx := range currentPlacements[i].cells {
bestBoard[idx] = byte('A' + i)
}
}
}
return
}
free := 0
r := c / m
col := c % m
rowBits := (^board[r]) & mMask & ^uint16((1<<col)-1)
free += bits.OnesCount16(rowBits)
for i := r + 1; i < n; i++ {
free += bits.OnesCount16((^board[i]) & mMask)
}
if pieces+free/5 <= maxPieces {
return
}
for _, p := range placements[c] {
fits := true
for i := r; i < n; i++ {
if (board[i] & p.mask[i]) != 0 {
fits = false
break
}
}
if fits {
newBoard := board
for i := r; i < n; i++ {
newBoard[i] |= p.mask[i]
}
currentPlacements[pieces] = p
solve(c+1, pieces+1, newBoard)
}
}
solve(c+1, pieces, board)
}
func main() {
if _, err := fmt.Scan(&n, &m); err != nil {
return
}
mMask = uint16((1 << m) - 1)
for i := 0; i < n*m; i++ {
bestBoard[i] = '.'
}
initPlacements()
var initialBoard [9]uint16
solve(0, 0, initialBoard)
fmt.Println(maxPieces)
for i := 0; i < n; i++ {
fmt.Println(string(bestBoard[i*m : (i+1)*m]))
}
}
```