Algorithmic approach
Define a fixed width x and hight y for the rectangles (grid cells). Split your polygon in horizontal slices (bands, belts) at the interval of y (red outlined in screenshot).
Get the intersection of the bands of step 1 with the initial polygon (highlighted in yellow).
Get the bounding box of the intersection (black dotted line).
Now split the black dotted polygon vertically with a horizontal distance of x to get the polygon in dark blue line pattern.
Repeat these steps to the right until you reach the end of the bounding box of step 3, then go to the next line (band) and continue until you have split up all bands like this.

Implementation using QGIS expressions
Use this expression with Geometry Generator or Geometry by expression to create the grid cells for the coverage layer. Define the width and length of the cells in lines 2 and 3 (see last screenshot at the bottom):

collect_geometries (
with_variable ('ver_dist',22800, -- change polygon width here
with_variable ('hor_dist',31000, -- change polygon length here
array_foreach (
generate_series (0,(y_max($geometry)-ymin($geometry))/@ver_dist),
with_variable (
'bb',
bounds (
intersection (
$geometry,
bounds(
make_line (
make_point (x_min($geometry), y_max($geometry)-@ver_dist*@element),
make_point (x_max($geometry), y_max($geometry)-@ver_dist*@element),
make_point (x_max($geometry), y_max($geometry)-@ver_dist*(@element+1))
)))),
collect_geometries (
array_foreach (
generate_series (0,(x_max(@bb)-x_min(@bb))/@hor_dist),
bounds(
make_line (
make_point (x_min(@bb)+@hor_dist*@element,y_min(@bb)),
make_point (x_min(@bb)+@hor_dist*@element,y_max(@bb)),
project (make_point (x_min(@bb)+@hor_dist*@element,y_max(@bb)), @hor_dist, radians (90))
)))))))))
Changing the values, you can easily create differnt cell sizes. Using geometry genrator, you can see the changes in realtime - good for testing an ideal cellsize:

Variants
If you choose smaller sizes for the rectangles, depending on the shape of the polygon to cover, there will be empty cell. To create only cells that intersect the polygon, modify the expression: replace the last 6 lines (starting with bounds(... with this expression:
with_variable(
'bs',
bounds(
make_line (
make_point (x_min(@bb)+@hor_dist*@element,y_min(@bb)),
make_point (x_min(@bb)+@hor_dist*@element,y_max(@bb)),
project (make_point (x_min(@bb)+@hor_dist*@element,y_max(@bb)), @hor_dist, radians (90)))),
case
when intersects (@bs, $geometry)
then @bs
else centroid ($geometry)
end
))))))))
Based on your comment: to avoid the grid cells in the last row to be smaller, instead of setting a fixed size for @ver_dist (vertical distance y), you can divide the whole north/south extent of the polygon in an even number so you have exactly matching hight of the grids to cover the whole polygon. You can than adopt the horizontal x distance to be the factor square root of 2 to have an aspect ratio corresponding to paper size A4, A3 etc. Modify lines 2 and 3 like this (multiply with sqrt(2) for landscape, divide by the same value for portrait orientation):
with_variable ('ver_dist', (y_max($geometry)-y_min($geometry))/5,
with_variable ('hor_dist',@ver_dist*sqrt(2), -- change multiply operator to divide operator for portrait (instead of landscape) orientation
Distance overlapping is normally done in the print composer when you set up the atlas, see next screenshot. If you want, you can do this already in the expression, to generate slightly overlapping polygons. Simply add a buffer at the last bounds function (6th last line in the above expression). I now set it to 10% of vertical distance, you can change the factor of 0.1 in the expression or set an absolute value for buffer size instead, as you like. See below for the modified expression below, including all variants discussed here.

I added an array_filter() function to avoid having an empty geometry, created to avoid empty polygons.
So the final expression, including the four variants, looks like this:
Screenshot, showing the overlapping buffers with 8 horizontal bands:

collect_geometries (
with_variable ('ver_dist', (y_max($geometry)-y_min($geometry))/8, -- change number of horizontal bands here
with_variable ('hor_dist',@ver_dist*sqrt(2), -- hange multiply operator to divide operator for portrait (instead of landscape) orientation
array_foreach (
generate_series (0,(y_max($geometry)-ymin($geometry))/@ver_dist),
with_variable (
'bb',
bounds (
intersection (
$geometry,
bounds(
make_line (
make_point (x_min($geometry), y_max($geometry)-@ver_dist*@element),
make_point (x_max($geometry), y_max($geometry)-@ver_dist*@element),
make_point (x_max($geometry), y_max($geometry)-@ver_dist*(@element+1))
)))),
collect_geometries (
array_filter (
array_foreach (
generate_series (0,(x_max(@bb)-x_min(@bb))/@hor_dist),
buffer (
with_variable(
'bs',
bounds(
make_line (
make_point (x_min(@bb)+@hor_dist*@element,y_min(@bb)),
make_point (x_min(@bb)+@hor_dist*@element,y_max(@bb)),
project (make_point (x_min(@bb)+@hor_dist*@element,y_max(@bb)), @hor_dist, radians (90))
)),
case
when intersects (@bs, $geometry)
then @bs
else NULL
end
),
@ver_dist*0.1, -- change size of overlap here, now set to 10% of vertical distance
join:='miter'
)),
@element is not NULL
)))))))
Variant with overlap without buffer
For your last comment, use this expression, where you can set fixed width/length for the rectangle and a value for overlap:

collect_geometries (
with_variable ('ver_dist', 20000, -- change number of horizontal bands here
with_variable ('hor_dist',25000, -- change multiply operator to divide operator for portrait (instead of landscape) orientation
with_variable ('overlap',1000, -- change overlap distance
array_foreach (
generate_series (1,ceil((y_max($geometry)-ymin($geometry))/@ver_dist)+1),
with_variable (
'bo',
bounds(
intersection (
$geometry,
bounds(
extrude(
make_line (
make_point (x_min($geometry), y_max($geometry)+@overlap*(@element)-@ver_dist*@element),
make_point (x_max($geometry), y_max($geometry)+@overlap*(@element)-@ver_dist*@element)
),
0,
@ver_dist
)))),
with_variable(
'bx',
bounds(
make_line (
project (make_point (x_min(@bo)-@overlap,y_max(@bo)+@overlap*(@element=1)),@ver_dist, radians(180)),
make_point (x_min(@bo)-@overlap,y_max(@bo)+@overlap*(@element=1)),
make_point (x_max(@bo)+@overlap,y_max(@bo)+@overlap*(@element=1))
)),
collect_geometries (
array_filter (
array_foreach (
generate_series (0,((x_max(@bx)-x_min(@bx))/@hor_dist)+1),
with_variable(
'bs',
bounds(
make_line (
make_point (x_min(@bx)-@overlap*(@element+1)+@hor_dist*@element,y_min(@bx)),
make_point (x_min(@bx)-@overlap*(@element+1)+@hor_dist*@element,y_max(@bx)),
project (make_point (x_min(@bx)-@overlap*(@element+1)+@hor_dist*@element,y_max(@bx)), @hor_dist, radians (90))
)),
case
when intersects (@bs, $geometry)
then @bs
else NULL
end
)),
@element is not NULL
)))))))))