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 PHP (7.4.4) (judge verdict: Time Limit Exceeded)

<?php


/**
 * Class LazySegmentTree
 * ref. AtCoder Library
 */
class LazySegmentTree
{
    private $tree = [];
    private array $lz;
    private int $leaf_size = 0;
    private int $tree_size = 0;
    private int $log;
    private $e;
    private object $mapping;
    private object $composition;
    private $id;
    private object $op;
    private $t = [0xFFFFFFFF00000000,
        0x00000000FFFF0000,
        0x000000000000FF00,
        0x00000000000000F0,
        0x000000000000000C,
        0x0000000000000002];

    /**
     * LazySegmentTree constructor.
     * @param int|array $sv int 配列の数 | array 初期配列
     * @param object $op operator : 範囲で値を取り出すときに作用させる関数
     * @param mixed $e :単位元 operator(単位元, a) = a, operator(b, 単位元) = b を満たす
     * @param object $mapping : 値の更新に用いる関数　mapping(value, tree_value) = new_tree_value
     * @param object $composition : 値の更新の重ね合わせに用いる関数 map(v1, map(v2, tree_value)) = map(comp(v1, v2), tree_value)を満たす関数
     * @param mixed $id 値の更新に用いる単位元 map(id, tree_value) = tree_valueを満たす
     */
    function __construct($sv, object $op, $e, object $mapping, object $composition, $id )
    {
        if(gettype($sv) === "integer"){
            $count = $sv;
            $list = array_fill(0, $count, $e);
        }elseif(gettype($sv) === "array"){
            $count = count($sv);
            $list = $sv;
        }else{
            $count = 0;
            $list = [];
        }
        $this->e = $e;
        $this->op = $op;
        $this->mapping = $mapping;
        $this->composition = $composition;
        $this->id = $id;
        $this->real_size = $count;
        $this->log = $this->ceil_log2($count);
        $this->leaf_size = 1 << $this->log;
        $this->tree_size = $this->leaf_size * 2 - 1;
        $this->lz = array_fill(0, $this->tree_size, $this->id);
        $tree = [...array_fill(0,$this->leaf_size, $this->e),
            ...$list,
            ...array_fill(0, $this->leaf_size - $count, $this->e)];
        $this->tree = SplFixedArray::fromArray($tree);
        for($i=$this->leaf_size-1; $i>=1;$i--){
            $this->update($i);
        }
    }

    /**
     * 値を更新する。配列の添え字は<em>0</em>スタート
     * 計算量 O(log_n)
     * @param int $p 更新する配列番号 0 ≤ p < 配列数
     * @param mixed $x 更新する値
     */
    public function set(int $p, $x):void
    {
        assert(0 <= $p && $p < $this->real_size);
        $p += $this->leaf_size;
        for($i=$this->log; $i >= 1; $i--){
            $this->push($p >> $i);
        }
        $this->tree[$p] = $x;
        for($i=1; $i<= $this->log; $i++){
            $this->update($p >> $i);
        }

    }

    /**
     * 値を取得する。配列の添え字は0スタート
     * 計算量 O(log_n)
     * @param int $p 値を取り出す配列番号　0 ≤ p < 配列数
     * @return mixed pの値
     */
    public function get(int $p)
    {
        assert(0 <= $p && $p < $this->real_size);
        $p += $this->leaf_size;
        for($i=$this->log; $i >= 1; $i--){
            $this->push($p >> $i);
        }
        return $this->tree[$p];
    }

    /**
     * op(a[l], a[l+1], ... , a[r-1])を計算します。 <br>
     * 配列は0スタートで、取得する値に<em>rは含まれない！</em>
     * 計算量 O(log_n)
     * @param int $l 0 ≤ l < n　
     * @param int $r l ≤ r < n
     * @return mixed
     */
    public function prod(int $l, int $r){
        assert(0 <= $l && $l <= $r && $r <= $this->real_size);
        if($l === $r) return $this->e;

        $l += $this->leaf_size;
        $r += $this->leaf_size;

        for ($i = $this->log; $i >= 1; $i--){
            if((($l >> $i) << $i) != $l) $this->push($l >> $i);
            if((($r >> $i) << $i) != $r) $this->push($r >> $i);

        }
        $sml = $smr = $this->e;
        while($l < $r){
            if($l & 1) $sml = ($this->op)($sml, $this->tree[$l++]);
            if($r & 1) $smr = ($this->op)($this->tree[--$r], $smr);
            $l >>= 1;
            $r >>= 1;
        }
        return ($this->op)($sml, $smr);
    }


    /**
     * op(a[0], ..., a[n - 1]) を計算します。
     * 計算量 O(1)
     * @return mixed
     */
    public function prod_all()
    {
        return $this->tree[1];
    }

    /**
     * 値の更新を行います。配列の添え字は<em>0</em>スタートで、<em>l～r-1</em>までを更新します。
     * i = l, l+1, ..., r-1に対し、
     * a[i] = mapping(x, a[i])を計算します。
     * l = r のとき、 a[l] = mapping(x, a[l])をより高速に計算します。
     * 計算量 O(log_n)
     * @param int $l 開始番号
     * @param int $r 終了番号+1
     * @param mixed $x 更新に用いる値
     */
    public function apply(int $l, int $r, $x):void
    {
        assert(0 <= $l && $l <= $r && $r <= $this->real_size);
        if($l === $r){
            $l += $this->leaf_size;
            for($i = $this->log; $i >= 1; $i--){
                $this->push($l >> $i);
            }
            $this->tree[$l] = ($this->mapping)($x, $this->tree[$l]);
            for($i=1;$i<=$this->log; $i++){
                $this->update($l>>$i);
            }
        }else{
            $l += $this->leaf_size;
            $r += $this->leaf_size;
            for($i = $this->log; $i >= 1; $i--){
                if ((($l >> $i) << $i ) != $l) $this->push($l >> $i);
                if ((($r >> $i) << $i ) != $r) $this->push(($r-1) >> $i);
            }
            $l2 = $l;
            $r2 = $r;
            while ($l < $r){
                if($l & 1) $this->all_apply($l++, $x);
                if($r & 1) $this->all_apply(--$r, $x);
                $l >>= 1;
                $r >>= 1;
            }
            $l = $l2;
            $r = $r2;

            for($i= 1; $i<=$this->log; $i++){
                if ((($l >> $i) << $i ) != $l) $this->update($l >> $i);
                if ((($r >> $i) << $i ) != $r) $this->update(($r-1) >> $i);
            }
        }
    }

    private function update(int $k)
    {
        $this->tree[$k] = ($this->op)($this->tree[2 * $k], $this->tree[2 * $k + 1]);
    }

    private function all_apply(int $k, $f):void
    {
//        var_dump(($this->mapping)($f, $this->tree[$k]));

        $this->tree[$k] = ($this->mapping)($f, $this->tree[$k]);

        if($k < $this->leaf_size) $this->lz[$k] = ($this->composition)($f, $this->lz[$k]);
    }

    private function push(int $k):void
    {
        if($this->lz[$k] === $this->id){
            return;
        }
        $this->all_apply(2 * $k, $this->lz[$k]);
        $this->all_apply(2 * $k + 1, $this->lz[$k]);
        $this->lz[$k] = $this->id;
    }



    /**
     * 以下の条件を両方満たす r を(いずれか一つ)返します。
     * r = l もしくは f(op(a[l], a[l + 1], ..., a[r - 1])) = true
     * r = n もしくは f(op(a[l], a[l + 1], ..., a[r])) = false
     * fが単調だとすれば、f(op(a[l], a[l + 1], ..., a[r - 1])) = true となる最大の r、と解釈することが可能です。
     * 添え字は0スタート
     * 計算量 O(log_n)
     * @param int $l
     * @param callable $fn 評価関数 fn(e) = true
     * @return int
     */
    public function max_right(int $l, callable $fn): int
    {
        assert(0 <= $l && $l <= $this->real_size);
        assert(($fn)($this->e));
        if($l === $this->real_size){
            return $this->real_size;
        }
        $l += $this->leaf_size;
        for($i = $this->log; $i>= 1;$i--){
            $this->push($l >> $i);
        }
        $sm = $this->e;
        do {
            while ($l % 2 === 0) {
                $l >>= 1;
            }
            if (!$fn(($this->op)($sm,$this->tree[$l]))) {
                while ($l < $this->leaf_size) {
                    $this->push($l);
                    $l <<= 1;

                    if ($fn(($this->op)($sm, $this->tree[$l]))) {
                        $sm = ($this->op)($sm, $this->tree[$l]);
                        $l++;
                    }
                }
                return $l - $this->leaf_size;
            }
            $sm = ($this->op)($sm, $this->tree[$l]);
            $l++;

        } while (($l & -$l) !== $l);
        return $this->real_size;
    }

    /**
     * 以下の条件を両方満たす l を(いずれか一つ)返します。
     * l = r もしくは f(op(a[l], a[l + 1], ..., a[r - 1])) = true
     * l = 0 もしくは f(op(a[l - 1], a[l + 1], ..., a[r - 1])) = false
     * fが単調だとすれば、f(op(a[l], a[l + 1], ..., a[r - 1])) = true となる最小の l、と解釈することが可能です。
     * 制約
     * fを同じ引数で呼んだ時、返り値は等しい(=副作用はない)
     * f(e()) = true
     * 0 ≤ r ≤ n
     * 計算量
     * O(log_n)
     * @param int $r
     * @param callable $fn
     * @return int
     */
    public function min_left(int $r, callable $fn): int
    {
        assert(0 <= $r && $r <= $this->real_size);
        assert($fn($this->e));
        if($r === 0){
            return 0;
        }
        $r += $this->leaf_size;
        $sm = $this->e;
        do {
            $r--;
            while ($r > 1 && ($r % 2)){
                $r >>= 1;
            }
            if (!$fn(($this->op)($this->tree[$r],$sm))) {
                while ($r < $this->leaf_size) {
                    $this->push($r);
                    $r *= 2;
                    $r += 1;
                    if ($fn(($this->op)($sm, $this->tree[$r]))) {
                        $sm = ($this->op)($sm, $this->tree[$r]);
                        $r--;
                    }
                }
                return $r - $this->leaf_size ;
            }
            $sm = ($this->op)($sm, $this->tree[$r]);
        } while (($r & -$r) !== $r);
        return 0;
    }

    private function ceil_log2(int $x):int
    {
        $y = ((($x & ($x - 1)) == 0) ? 0 : 1);
        $j = 32;
        foreach ($this->t as $value) {
            $k = ((($x & $value) == 0) ? 0 : $j);
            $y += $k;
            $x >>= $k;
            $j >>= 1;
        }
        return $y;
    }
}

const MOD = 998244353;
[$N, $Q] = fscanf(STDIN, "%d%d");
$A = fscanf(STDIN, str_repeat("%d", $N));
foreach($A as &$value){
    $value = [$value, 1];
}


$op = fn($l, $r) => [($l[0] + $r[0])% MOD, $l[1] + $r[1]];
$e = [0,0];
$map = fn($l, $r) => [($r[0] * $l[0] % MOD + $r[1] * $l[1] %MOD)%MOD, $r[1]];
$comp = fn($l, $r) => [$r[0] * $l[0] %MOD,( $r[1] * $l[0] + $l[1])%MOD];
$id = [1,0];
//$fn = fn($x, $y) => max($x, $y);


$st = new LazySegmentTree($A,$op,$e, $map,$comp,$id);

while($Q--){
    [$T, $L, $R, $C, $D] = fscanf(STDIN, "%d%d%d%d%d");
    if($T === 0){
        $st->apply($L, $R, [$C,$D]);
    }else{
        $a  = $st->prod($L, $R)[0];
        echo $a.PHP_EOL;
    }
}