Problem:
Score : 100 points

Problem Statement

You are given an array a_0, a_1, ..., a_{N-1} of length N. Process Q queries of the following types.

0 l r b c: For each i = l, l+1, \dots, {r - 1}, set a_i \gets b \times a_i + c.

1 l r: Print \sum_{i = l}^{r - 1} a_i \bmod 998244353.

Constraints

1 \leq N, Q \leq 500000

0 \leq a_i, c < 998244353

1 \leq b < 998244353

0 \leq l < r \leq N

All values in Input are integer.

Input

Input is given from Standard Input in the following format:

N Q
a_0 a_1 ... a_{N - 1}
\textrm{Query}_0
\textrm{Query}_1
:
\textrm{Query}_{Q - 1}

Output

For each query of the latter type, print the answer.

Sample Input 1

5 7
1 2 3 4 5
1 0 5
0 2 4 100 101
1 0 3
0 1 3 102 103
1 2 5
0 2 5 104 105
1 0 5

Sample Output 1

15
404
41511
4317767

Solution in Ruby (2.7.1) (judge verdict: Time Limit Exceeded)

class Lazy_SegTree
    def initialize(v, e, id, &op)
        @n = v.size
        @e = e
        @id = id
        @op = op
        @log = (@n-1).bit_length
        @size = 1 << @log
        @d = Array.new(2 * @size, e)
        @lz = Array.new(@size, id)

        @n.times {|i| @d[@size + i] = v[i]}
        (@size - 1).downto(1) {|i| update(i)}
    end

    def set_mapping(&mapping); @mapping = mapping; end
    def set_composition(&composition); @composition = composition; end
    def update(k); @d[k] = @op.call(@d[2 * k], @d[2 * k + 1]); end

    def set(pos, x)
        pos += @size
        @log.downto(1) {|i| push(pos >> i)}
        @d[pos] = x
        1.upto(@log) {|i| update(pos >> i)}
    end

    def get(pos)
        pos += @size
        @log.downto(1) {|i| push(pos >> i)}
        @d[pos]
    end

    def push(k)
        all_apply(2 * k, @lz[k])
        all_apply(2 * k + 1, @lz[k])
        @lz[k] = @id
    end

    def prod(l, r)
        return @e if l == r
        l += @size
        r += @size

        @log.downto(1) do |i|
            push(l >> i) if (l >> i) << i != l
            push(r >> i) if (r >> i) << i != r
        end

        sml = smr = @e
        while l < r
            (sml = @op.call(sml, @d[l]); l += 1) if l[0] == 1
            smr = @op.call(@d[r -= 1], smr) if r[0] == 1
            l >>= 1
            r >>= 1
        end

        @op.call(sml, smr)
    end

    def all_prod; @d[1]; end

    def all_apply(k, f) 
        @d[k] = @mapping.call(f, @d[k])
        @lz[k] = @composition.call(f, @lz[k]) if k < @size
    end

    def apply(pos, f)
        pos += @size
        @log.downto(1) {|i| push(pos >> i)}
        @d[pos] = @mapping.call(f, @d[pos])
        1.upto(@log) {|i| update(pos >> i)}
    end

    def range_apply(l, r, f)
        return if l == r
        l += @size
        r += @size

        @log.downto(1) do |i|
            push(l >> i) if (l >> i) << i != l
            push((r - 1) >> i) if (r >> i) << i != r
        end

        l2, r2 = l, r
        while l < r
            (all_apply(l, f); l += 1) if l[0] == 1
            all_apply(r -= 1, f) if r[0] == 1
            l >>= 1
            r >>= 1
        end

        l, r = l2, r2
        1.upto(@log) do |i|
            update(l >> i) if (l >> i) << i != l
            update((r - 1) >> i) if (r >> i) << i != r
        end
    end

    def max_right(l, &g)
        return @n if l == @n
        l += @size
        @log.downto(1) {|i| push(l >> i)}
        sm = @e

        loop do
            l >>= 1 while l[0] == 0
            if !g.call(@op.call(sm, @d[l]))
                while l < @size
                    push(l)
                    l <<= 1
                    if g.call(@op.call(sm, @d[l]))
                        sm = @op.call(sm, @d[l])
                        l += 1
                    end
                end
                return l - @size
            end
            sm = @op.call(sm, @d[l])
            l += 1
            break if l & -l == l
        end
        return @n
    end

    def min_left(r, &g)
        return 0 if r == 0
        r += @size
        @log.downto(1) {|i| push((r - 1) >> i)}
        sm = @e

        loop do
            r -= 1
            r >>= 1 while r > 1 && r[0] == 1
            if !g.call(@op.call(@d[r], sm))
                while r < @size
                    push(r)
                    r = 2 * r + 1
                    if g.call(@op.call(@d[r], sm))
                        sm = @op.call(@d[r], sm)
                        r -= 1
                    end
                end
                return r + 1 - @size
            end
            sm = @op.call(@d[r], sm)
            break if r & -r == r
        end
        return 0
    end
end

M = 998244353
n, q = gets.split.map(&:to_i)
a = gets.split.map{|e| [e.to_i, 1]}

lst = Lazy_SegTree.new(a, [0, 0], [1, 0]) {|ch1, ch2| [(ch1[0] + ch2[0]) % M, ch1[1] + ch2[1]]}
lst.set_mapping{|(b, c), (a, s)| [(a * b + c * s) % M, s]}
lst.set_composition{|(b1, c1), (b2, c2)| [(b2 * b1) % M, (c2 * b1 + c1) % M]}

q.times do
    t, l, r, a, b = gets.split.map(&:to_i)

    if t == 0
        lst.range_apply(l, r, [a, b])
    else
        puts lst.prod(l, r)[0]
    end
end

__END__
@op : S * S -> S : 子の情報を元に親情報の更新する関数
@mapping : F * S -> S : @lz に保存しておいた更新情報(F) を セグ木のノード(S) に反映する関数．S が葉ノードとは限らないことに注意
@composition : F * F -> F : 更新情報を伝搬させる関数（降りてきた情報が第一引数．今の情報は第二引数）???

@e : @op の底．つまり @op(e, x) = @op(x, e) = x
@id : @composition (? と @mapping) の底．つまり
    (? @mapping(id, x) = @mapping(x, id) = x かつ)
    @composition(id, x) = @composition(x, id) = x