Problem:


Score : 1600 points

Problem Statement

Ringo got interested in modern art. He decided to draw a big picture on the board with N+2 rows and M+2 columns of squares constructed in the venue of CODE FESTIVAL 2017, using some people.

The square at the (i+1)-th row and (j+1)-th column in the board is represented by the pair of integers (i,j). That is, the top-left square is (0,0), and the bottom-right square is (N+1,M+1).
Initially, the squares (x,y) satisfying 1 \leq x \leq N and 1 \leq y \leq M are painted white, and the other (outermost) squares are painted black.

Ringo arranged people at some of the outermost squares, facing inward.
More specifically, the arrangement of people is represented by four strings A, B, C and D, as follows:

For each row except the top and bottom, if the i-th character (1 \leq i \leq N) in A is 1, place a person facing right at the square (i,0); otherwise, do nothing.

For each row except the top and bottom, if the i-th character (1 \leq i \leq N) in B is 1, place a person facing left at the square (i,M+1); otherwise, do nothing.

For each column except the leftmost and rightmost, if the i-th character (1 \leq i \leq M) in C is 1, place a person facing down at the square (0,i); otherwise, do nothing.

For each column except the leftmost and rightmost, if the i-th character (1 \leq i \leq M) in D is 1, place a person facing up at the square (N+1,i); otherwise, do nothing.

Each person has a sufficient amount of non-white paint. No two people have paint of the same color.

An example of an arrangement of people (For convenience, black squares are displayed in gray)

Ringo repeats the following sequence of operations until all people are dismissed from the venue.

Select a person who is still in the venue.

The selected person repeats the following action while the square in front of him/her is white: move one square forward, and paint the square he/she enters with his/her paint. When the square in front of him/her is not white, he/she stops doing the action.

The person is now dismissed from the venue.

An example of a way the board is painted

How many different states of the board can Ringo obtain at the end of the process? Find the count modulo 998244353.

Two states of the board are considered different when there is a square painted in different colors.

Constraints

1 \leq N,M \leq 10^5

|A|=|B|=N

|C|=|D|=M

A, B, C and D consist of 0 and 1.

Input

Input is given from Standard Input in the following format:

N M
A
B
C
D

Output

Print the number of the different states of the board Ringo can obtain at the end of the process, modulo 998244353.

Sample Input 1

2 2
10
01
10
01

Sample Output 1

6

There are six possible states as shown below.

Sample Input 2

2 2
11
11
11
11

Sample Output 2

32

Sample Input 3

3 4
111
111
1111
1111

Sample Output 3

1276

Sample Input 4

17 21
11001010101011101
11001010011010111
111010101110101111100
011010110110101000111

Sample Output 4

548356548

Be sure to find the count modulo 998244353.

Sample Input 5

3 4
000
101
1111
0010

Sample Output 5

21

Sample Input 6

9 13
111100001
010101011
0000000000000
1010111111101

Sample Output 6

177856

Sample Input 7

23 30
01010010101010010001110
11010100100100101010101
000101001001010010101010101101
101001000100101001010010101000

Sample Output 7

734524988



Sample input:
2 2
10
01
10
01


Sample output:
6

Submission status: Accepted

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A,B,C,D = [np.array(list(readline().rstrip()))-48 for _ in range(4)]

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB=A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L=C.sum(); LU=A.cumsum()-A; LD=B.cumsum()-B
    NL=fact[LU+LD+L-1]*fact_inv[LU+LD]%MOD*fact_inv[L-1]% MOD

    R=D.sum(); RU=A[::-1].cumsum()-A[::-1]; RD=B[::-1].cumsum()-B[::-1]
    NR=fact[RU+RD+R-1]*fact_inv[RU+RD]%MOD*fact_inv[R-1]%MOD; NR=NR[::-1]

    x=np.ones(N,np.int64); x[AB==2]=2; coef=cumprod(x,MOD)
    x=np.ones(N,np.int64); x[AB==2]=(MOD+1)//2; coef_inv=cumprod(x,MOD)
    NL*=coef_inv; NR*=coef; NL%=MOD; NR%=MOD
    NL[AB==2]*=2
    NL[AB==0]=0;NR[AB==0]=0
    NL_cum=NL.cumsum()%MOD
    return (NL_cum*NR%MOD).sum()%MOD

def AB_only(N,A,B):
    x = np.ones(N,np.int64)
    x[A+B==2] = 2
    return cumprod(x,MOD)[-1]

def ABC_only(N,M,A,B,C):
    AB=A+B
    NA,NB,NC = [np.count_nonzero(x) for x in [A,B,C]]
    R=NC;RU=A[::-1].cumsum()-A[::-1];RD=B[::-1].cumsum()-B[::-1]
    NR=fact[RU+RD+R-1]*fact_inv[RU+RD]%MOD*fact_inv[R-1]%MOD; NR=NR[::-1]
    NR[AB==0]=0
    x=np.ones(N,np.int64); x[AB==2]=2; coef=cumprod(x,MOD)
    x=(coef*NR% MOD).sum()%MOD
    y=fact[NA+NB+NC-1]*fact_inv[NC-1]%MOD*fact_inv[NA+NB]%MOD
    return (x+y)%MOD

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A; C = C[::-1]; D = D[::-1]
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C; A = A[::-1]; B = B[::-1]
    if NB == 0:
        return AB_only(M,C,D)
    if ND == 0:
        return AB_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return ABC_only(M,N,C,D,B)
    if NC == 0:
        return ABC_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

Submission status: Accepted

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip()),dtype=np.int64) - 48
B = np.array(list(readline().rstrip()),dtype=np.int64) - 48
C = np.array(list(readline().rstrip()),dtype=np.int64) - 48
D = np.array(list(readline().rstrip()),dtype=np.int64) - 48

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    if L >= 1:
        NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
    else:
        NL = np.zeros(N,dtype=np.int64)
        i = np.where(AB>0)[0][0]
        NL[i] = 1
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]

    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def AB_only(N,A,B):
    x = np.ones(N,dtype=np.int64)
    x[A+B==2] = 2
    return cumprod(x,MOD)[-1]

def ABC_only(N,M,A,B,C):
    AB = A+B
    R = C.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
    NR[AB==0] = 0
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x=(coef * NR % MOD).sum()%MOD
    assert x == cnt_vertical(N,M,A,B,np.zeros_like(C),C)
    n = A.sum()+B.sum()+C.sum()-1; m = C.sum()-1
    y = fact[n] * fact_inv[m] % MOD * fact_inv[n-m] % MOD
    return (x+y)%MOD

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A; C = C[::-1]; D = D[::-1]
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C; A = A[::-1]; B = B[::-1]
    if NB == 0:
        return AB_only(M,C,D)
    if ND == 0:
        return AB_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return ABC_only(M,N,C,D,B)
    if NC == 0:
        return ABC_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

N=3
M=2
while True:
    A=np.random.randint(0,2,N).astype(np.int64)
    B=np.random.randint(0,2,N).astype(np.int64)
    C=np.random.randint(0,2,M).astype(np.int64)
    D=np.random.randint(0,2,M).astype(np.int64)
    x=F(N,M,A,B,C,D)
    y=F(M,N,C,D,A,B)
    z=F(N,M,B,A,C,D)
    if x != z:
        break

A,B,C,D

x,y,z

cnt_vertical(N,M,A,B,C,D),cnt_vertical(N,M,B,A,C,D)

cnt_vertical(M,N,C,D,A,B),cnt_vertical(M,N,C,D,B,A)

Submission status: Runtime Error

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip())) - 48
B = np.array(list(readline().rstrip())) - 48
C = np.array(list(readline().rstrip())) - 48
D = np.array(list(readline().rstrip())) - 48

A,B,C,D

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    if L >= 1:
        NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
    else:
        NL = np.zeros(N,dtype=np.int64)
        i = np.where(AB>0)[0][0]
        NL[i] = 1
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
    # とりあえず
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def AB_only(N,A,B):
    x = np.ones(N,dtype=np.int64)
    x[A+B==2] = 2
    return cumprod(x,MOD)[-1]

def ABC_only(N,M,A,B,C):
    raise Exception
    x = cnt_vertical(N,M,A,B,np.zeros_like(C),C)
    x += cnt_vertical(M,N,C,np.zeros_like(C),A,B)
    return x%MOD

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C
    if NB == 0:
        return AB_only(M,C,D)
    if ND == 0:
        return AB_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return ABC_only(M,N,C,D,B)
    if NC == 0:
        return ABC_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

Submission status: Runtime Error

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip())) - 48
B = np.array(list(readline().rstrip())) - 48
C = np.array(list(readline().rstrip())) - 48
D = np.array(list(readline().rstrip())) - 48

A,B,C,D

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    if L >= 1:
        NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
    else:
        NL = np.zeros(N,dtype=np.int64)
        i = np.where(AB>0)[0][0]
        NL[i] = 1
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
    # とりあえず
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def AB_only(N,A,B):
    raise Exception
    x = np.ones(N,dtype=np.int64)
    x[A+B==2] = 2
    return cumprod(x,MOD)[-1]

def ABC_only(N,M,A,B,C):
    x = cnt_vertical(N,M,A,B,np.zeros_like(C),C)
    x += cnt_vertical(M,N,C,np.zeros_like(C),A,B)
    return x%MOD

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C
    if NB == 0:
        return AB_only(M,C,D)
    if ND == 0:
        return AB_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return ABC_only(M,N,C,D,B)
    if NC == 0:
        return ABC_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

Submission status: Runtime Error

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip())) - 48
B = np.array(list(readline().rstrip())) - 48
C = np.array(list(readline().rstrip())) - 48
D = np.array(list(readline().rstrip())) - 48

A,B,C,D

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    if L >= 1:
        NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
    else:
        NL = np.zeros(N,dtype=np.int64)
        i = np.where(AB>0)[0][0]
        NL[i] = 1
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
    # とりあえず
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def AB_only(N,A,B):
    x = np.ones(N,1,np.int64)
    x[A+B==2] = 2
    return cumprod(x,MOD)[-1]

def ABC_only(N,M,A,B,C):
    x = cnt_vertical(N,M,A,B,np.zeros_like(C),C)
    x += cnt_vertical(M,N,C,np.zeros_like(C),A,B)
    return x%MOD

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C
    if NB == 0:
        return AB_only(M,C,D)
    if ND == 0:
        return AB_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return ABC_only(M,N,C,D,B)
    if NC == 0:
        return ABC_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

Submission status: Runtime Error

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip())) - 48
B = np.array(list(readline().rstrip())) - 48
C = np.array(list(readline().rstrip())) - 48
D = np.array(list(readline().rstrip())) - 48

A,B,C,D

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    if L >= 1:
        NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
    else:
        NL = np.zeros(N,dtype=np.int64)
        i = np.where(AB>0)[0][0]
        NL[i] = 1
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
    # とりあえず
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def AB_only(N,A,B):
    x = np.ones(N,1,np.int64)
    x[A+B==2] = 2
    return cumprod(x,MOD)[-1]

def ABC_only(N,M,A,B,C):
    x = F(N,M,A,B,np.zeros_like(C),C)
    x += F(N,M,C,np.zeros_like(C),A,B)
    raise x%MOD

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
#    if all(x != 0 for x in [NA,NB,NC,ND]):
    return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C
    if NB == 0:
        return AB_only(M,C,D)
    if ND == 0:
        return AB_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return ABC_only(M,N,C,D,B)
    if NC == 0:
        return ABC_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

Submission status: Runtime Error

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip())) - 48
B = np.array(list(readline().rstrip())) - 48
C = np.array(list(readline().rstrip())) - 48
D = np.array(list(readline().rstrip())) - 48

A,B,C,D

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
        
    # とりあえず
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def AB_only(N,A,B):
    x = np.ones(N,1,np.int64)
    x[A+B==2] = 2
    return cumprod(x,MOD)[-1]

def ABC_only(N,M,A,B,C):
    raise Exception

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C
    if NB == 0:
        return LR_only(M,C,D)
    if ND == 0:
        return LR_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return LRU_only(M,N,C,D,B)
    if NC == 0:
        return LRU_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

Submission status: Runtime Error

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip())) - 48
B = np.array(list(readline().rstrip())) - 48
C = np.array(list(readline().rstrip())) - 48
D = np.array(list(readline().rstrip())) - 48

A,B,C,D

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
        
    # とりあえず
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def LR_only(N,A,B):
    raise Exception

def LRU_only(N,M,A,B,C):
    raise Exception

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    # A,Cに0列を寄せる
    if NA != 0:
        NA,NB = NB,NA; A,B = B,A
    if NC != 0:
        NC,ND = ND,NC; C,D = D,C
    if NB == 0:
        return LR_only(M,C,D)
    if ND == 0:
        return LR_only(N,A,B)
    # B,Dは0ではない
    if NA == 0 and NC == 0:
        # 2面B,Dのみ -> binom(B+D,B)
        return fact[NB+ND] * fact_inv[NB] % MOD * fact_inv[ND] % MOD
    if NA == 0:
        return LRU_only(M,N,C,D,B)
    if NC == 0:
        return LRU_only(N,M,A,B,D)

answer = F(N,M,A,B,C,D)
print(answer)

Submission status: Runtime Error

Solution code:
import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

import numpy as np

MOD = 998244353

N,M = map(int,readline().split())
A = np.array(list(readline().rstrip())) - 48
B = np.array(list(readline().rstrip())) - 48
C = np.array(list(readline().rstrip())) - 48
D = np.array(list(readline().rstrip())) - 48

A,B,C,D

def cumprod(arr,MOD):
    L = len(arr); Lsq = int(L**.5+1)
    arr = np.resize(arr,Lsq**2).reshape(Lsq,Lsq)
    for n in range(1,Lsq):
        arr[:,n] *= arr[:,n-1]; arr[:,n] %= MOD
    for n in range(1,Lsq):
        arr[n] *= arr[n-1,-1]; arr[n] %= MOD
    return arr.ravel()[:L]

def make_fact(U,MOD):
    x = np.arange(U,dtype=np.int64); x[0] = 1
    fact = cumprod(x,MOD)
    x = np.arange(U,0,-1,dtype=np.int64); x[0] = pow(int(fact[-1]),MOD-2,MOD)
    fact_inv = cumprod(x,MOD)[::-1]
    return fact,fact_inv

U = 10**6
fact, fact_inv = make_fact(U,MOD)

def cnt_vertical(N,M,A,B,C,D):
    """
    ・ABが長さN、CDが長さM。
    ・初手に縦線を入れる場合。
    ・縦線の右端の位置に応じて場合分けして集計
    ・右端の位置を決めると、（左端の位置ごとの左側の入れ方の累積和） x （右側の入れ方の累積和）、適当に重みをつける必要あり
    """
    AB = A+B
    # ある場所を初めての縦線としたときに、左側にできる部分。上にある点の個数と下にある点の個数
    L = C.sum(); LU = A.cumsum() - A; LD = B.cumsum() - B
    NL = fact[LU+LD+L-1] * fact_inv[LU+LD] % MOD * fact_inv[L-1] % MOD
        
    R = D.sum(); RU = A[::-1].cumsum() - A[::-1]; RD = B[::-1].cumsum() - B[::-1]
    NR = fact[RU+RD+R-1] * fact_inv[RU+RD] % MOD * fact_inv[R-1] % MOD; NR = NR[::-1]
        
    # とりあえず
    x = np.full(N,1,np.int64); x[AB==2] = 2; coef = cumprod(x,MOD)
    x = np.full(N,1,np.int64); x[AB==2] = (MOD+1)//2; coef_inv = cumprod(x,MOD)
    NL *= coef_inv; NR *= coef; NL %= MOD; NR %= MOD
    NL[AB==2] *= 2; NL[NL>=MOD] -= MOD
    NL[AB==0] = 0; NR[AB==0] = 0
    # sum(l<=r) NL[l] NR[r] を求めればよい。
    NL_cum = NL.cumsum() % MOD
    return (NL_cum * NR % MOD).sum() % MOD

def F(N,M,A,B,C,D):
    # 問題の答えを出します。
    NA,NB,NC,ND = [np.count_nonzero(x) for x in [A,B,C,D]]
    if all(x != 0 for x in [NA,NB,NC,ND]):
        return (cnt_vertical(N,M,A,B,C,D) + cnt_vertical(M,N,C,D,A,B)) % MOD
    raise Exception

answer = F(N,M,A,B,C,D)
print(answer)