#![doc = " # Bundled libraries"]
#![doc = ""]
#![doc = " ## `kyopro_lib` (private)"]
#![doc = ""]
#![doc = " ### Modules"]
#![doc = ""]
#![doc = " - `::__kyopro_lib::modint` → `$crate::modint`"]
#![doc = " - `::__kyopro_lib::segtree` → `$crate::segtree`"]
#[allow(unused_imports)]
use itertools::Itertools;
use proconio::input;
#[allow(unused_imports)]
use proconio::marker::*;
type Mint = Mint998_244_353;
fn main() {
    input! { n : usize , q : usize , a : [i64 ; n] , }
    let mut st = LazySegTree::build_from_iter(
        a.iter().map(|a| Mint::from(*a)),
        Mint::new(0),
        |&a, &b| a + b,
        |a: &(Mint, Mint), b| (a.0 * b.0, b.0 * a.1 + b.1),
        |&a, b, l| b.0 * a + b.1 * Mint::from(l),
    );
    for _ in 0..q {
        input! { t : u8 }
        if t == 0 {
            input! { l : usize , r : usize , b : u64 , c : u64 }
            st.update_range(l..r, (Mint::new(b), Mint::new(c)));
        } else {
            input! { l : usize , r : usize }
            println!("{}", st.query(l..r));
        }
    }
}
use self::modint::Mint998_244_353;
use self::segtree::LazySegTree;
pub mod modint {
    pub trait Modulation {
        const MOD: u64;
    }
    #[derive(PartialEq, Eq)]
    pub struct ModInt<M: Modulation> {
        x: u64,
        phantom: std::marker::PhantomData<M>,
    }
    impl<M: Modulation> Clone for ModInt<M> {
        fn clone(&self) -> Self {
            Self::new_uncheck(self.x)
        }
    }
    impl<M: Modulation> Copy for ModInt<M> {}
    impl<M: Modulation> std::fmt::Display for ModInt<M> {
        fn fmt(&self, dest: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
            write!(dest, "{}", self.x)
        }
    }
    impl<M: Modulation> ModInt<M> {
        #[inline]
        pub fn new(x: u64) -> Self {
            Self {
                x: x % M::MOD,
                phantom: std::marker::PhantomData,
            }
        }
        #[inline]
        pub fn from_signed(x: i64) -> Self {
            let x = if x >= 0 {
                x as u64 % M::MOD
            } else {
                (M::MOD as i64 + x % M::MOD as i64) as u64
            };
            Self {
                x: x,
                phantom: std::marker::PhantomData,
            }
        }
        #[inline]
        pub fn new_uncheck(x: u64) -> Self {
            Self {
                x: x,
                phantom: std::marker::PhantomData,
            }
        }
        #[inline]
        pub fn val(&self) -> u64 {
            self.x
        }
    }
    macro_rules! def_from_trait {
        (u , $ t : ty) => {
            impl<M: Modulation> std::convert::From<$t> for ModInt<M> {
                #[inline]
                fn from(x: $t) -> Self {
                    Self::new(x as u64)
                }
            }
        };
        (i , $ t : ty) => {
            impl<M: Modulation> std::convert::From<$t> for ModInt<M> {
                #[inline]
                fn from(x: $t) -> Self {
                    Self::from_signed(x as i64)
                }
            }
        };
    }
    def_from_trait!(u, u8);
    def_from_trait!(u, u16);
    def_from_trait!(u, u32);
    def_from_trait!(u, u64);
    def_from_trait!(u, usize);
    def_from_trait!(i, i8);
    def_from_trait!(i, i16);
    def_from_trait!(i, i32);
    def_from_trait!(i, i64);
    def_from_trait!(i, isize);
    impl<M: Modulation> std::ops::Neg for ModInt<M> {
        type Output = Self;
        #[inline]
        fn neg(self) -> Self {
            Self {
                x: if self.x == 0 { 0 } else { M::MOD - self.x },
                phantom: std::marker::PhantomData,
            }
        }
    }
    impl<M: Modulation> std::ops::AddAssign for ModInt<M> {
        #[inline]
        fn add_assign(&mut self, rhs: Self) {
            self.x += rhs.x;
            if M::MOD <= self.x {
                self.x -= M::MOD;
            }
        }
    }
    impl<M: Modulation> std::ops::Add for ModInt<M> {
        type Output = Self;
        #[inline]
        fn add(self, rhs: Self) -> Self {
            let mut res = self;
            res += rhs;
            res
        }
    }
    impl<M: Modulation> std::ops::SubAssign for ModInt<M> {
        #[inline]
        fn sub_assign(&mut self, rhs: Self) {
            if self.x >= rhs.x {
                self.x -= rhs.x;
            } else {
                self.x += M::MOD - rhs.x;
            }
        }
    }
    impl<M: Modulation> std::ops::Sub for ModInt<M> {
        type Output = Self;
        #[inline]
        fn sub(self, rhs: Self) -> Self {
            let mut res = self;
            res -= rhs;
            res
        }
    }
    impl<M: Modulation> std::ops::MulAssign for ModInt<M> {
        #[inline]
        fn mul_assign(&mut self, rhs: Self) {
            self.x *= rhs.x;
            self.x %= M::MOD;
        }
    }
    impl<M: Modulation> std::ops::Mul for ModInt<M> {
        type Output = Self;
        #[inline]
        fn mul(self, rhs: Self) -> Self {
            let mut res = self;
            res *= rhs;
            res
        }
    }
    fn ext_gcd(a: u64, b: u64) -> (i64, i64, u64) {
        if b == 0 {
            (1, 0, a)
        } else {
            let (mut y, x, d) = ext_gcd(b, a % b);
            y -= (a / b) as i64 * x;
            (x, y, d)
        }
    }
    impl<M: Modulation> ModInt<M> {
        pub fn pow(&self, mut n: u64) -> Self {
            let mut res = Self::new_uncheck(1);
            let mut d = *self;
            while n != 0 {
                if n & 1 != 0 {
                    res *= d;
                }
                d *= d;
                n >>= 1;
            }
            res
        }
        pub fn inv(&self) -> Self {
            let (mut x, _, _) = ext_gcd(self.x, M::MOD);
            if x < 0 {
                x %= M::MOD as i64;
                x += M::MOD as i64;
            }
            Self::new(x as u64)
        }
        pub fn gen_factorial(n: u64) -> Factorial<M> {
            let n = n as usize;
            let mut pos = vec![Self::new_uncheck(1); n + 1];
            let mut neg = vec![Self::new_uncheck(1); n + 1];
            for i in 1..n {
                pos[i + 1] = pos[i] * Self::new(i as u64 + 1);
            }
            neg[n] = pos[n].inv();
            for i in (1..n).rev() {
                neg[i] = neg[i + 1] * Self::new(i as u64 + 1);
            }
            Factorial { pos: pos, neg: neg }
        }
        pub fn gen_inv(n: u64) -> Vec<Self> {
            let mut res = vec![Self::new_uncheck(1); (n + 1) as usize];
            for i in 2..=n {
                res[i as usize] =
                    Self::new_uncheck(M::MOD - M::MOD / i) * res[(M::MOD % i) as usize];
            }
            res
        }
    }
    pub struct Factorial<M: Modulation> {
        pos: Vec<ModInt<M>>,
        neg: Vec<ModInt<M>>,
    }
    impl<M: Modulation> Factorial<M> {
        pub fn permutation(&self, n: u64, r: u64) -> ModInt<M> {
            if r > n {
                ModInt::new(0)
            } else {
                self.pos[n as usize] * self.neg[(n - r) as usize]
            }
        }
        pub fn binomial(&self, n: u64, r: u64) -> ModInt<M> {
            if r > n {
                ModInt::new(0)
            } else {
                self.pos[n as usize] * self.neg[r as usize] * self.neg[(n - r) as usize]
            }
        }
        pub fn cmb_with_rep(&self, n: u64, r: u64) -> ModInt<M> {
            self.binomial(n + r - 1, r)
        }
    }
    pub struct F1_000_000_007;
    impl Modulation for F1_000_000_007 {
        const MOD: u64 = 1_000_000_007;
    }
    pub type Mint1_000_000_007 = ModInt<F1_000_000_007>;
    pub struct F998_244_353;
    impl Modulation for F998_244_353 {
        const MOD: u64 = 998_244_353;
    }
    pub type Mint998_244_353 = ModInt<F998_244_353>;
}
pub mod segtree {
    use std::ops::Range;
    use std::ops::RangeBounds;
    fn bound_to_range<R: RangeBounds<usize>>(r1: R, r2: Range<usize>) -> Range<usize> {
        use std::ops::Bound;
        let s = match r1.start_bound() {
            Bound::Included(&s) => s,
            Bound::Excluded(&s) => s + 1,
            Bound::Unbounded => r2.start,
        };
        let e = match r1.end_bound() {
            Bound::Included(&e) => e + 1,
            Bound::Excluded(&e) => e,
            Bound::Unbounded => r2.end,
        };
        std::cmp::max(s, r2.start)..std::cmp::min(e, r2.end)
    }
    fn is_overlap(r1: &Range<usize>, r2: &Range<usize>) -> bool {
        !(r2.end <= r1.start || r1.end <= r2.start)
    }
    fn is_include(r1: &Range<usize>, r2: &Range<usize>) -> bool {
        r1.start <= r2.start && r2.end <= r1.end
    }
    pub struct SegTree<T, F> {
        n: usize,
        id: T,
        dat: Vec<T>,
        f: F,
    }
    impl<T, F> SegTree<T, F>
    where
        T: Clone,
        F: Fn(&T, &T) -> T,
    {
        pub fn new(n: usize, id: T, f: F) -> Self {
            let n = n.next_power_of_two();
            Self {
                n: n,
                id: id.clone(),
                dat: vec![id; n * 2 - 1],
                f: f,
            }
        }
        pub fn build_from_slice(dat: &[T], id: T, f: F) -> Self {
            let n = dat.len().next_power_of_two();
            let mut v = Vec::with_capacity(2 * n - 1);
            v.resize(n - 1, id.clone());
            v.extend_from_slice(dat);
            v.resize(2 * n - 1, id.clone());
            let mut st = Self {
                n: n,
                id: id,
                dat: v,
                f: f,
            };
            for i in (0..n - 1).rev() {
                st.update_at(i);
            }
            st
        }
        pub fn build_from_iter<I: Iterator<Item = T>>(iter: I, id: T, f: F) -> Self {
            use itertools::Itertools;
            Self::build_from_slice(&iter.collect_vec(), id, f)
        }
        #[inline]
        pub fn len(&self) -> usize {
            self.n
        }
        #[inline]
        pub fn get_element(&self, i: usize) -> &T {
            &self.dat[self.n + i - 1]
        }
        #[inline]
        pub fn as_slice(&self) -> &[T] {
            &self.dat[self.n - 1..]
        }
        pub fn update(&mut self, i: usize, dat: T) {
            let i = self.n + i - 1;
            self.dat[i] = dat;
            self.update_to_bottom_up(i);
        }
        pub fn update_by<F2: Fn(&mut T)>(&mut self, i: usize, f: F2) {
            let i = self.n + i - 1;
            f(&mut self.dat[i]);
            self.update_to_bottom_up(i);
        }
        #[inline]
        fn update_at(&mut self, i: usize) {
            self.dat[i] = (self.f)(&self.dat[i * 2 + 1], &self.dat[i * 2 + 2]);
        }
        #[inline]
        fn update_to_bottom_up(&mut self, mut i: usize) {
            while i != 0 {
                i = (i - 1) / 2;
                self.update_at(i);
            }
        }
        pub fn query<R: RangeBounds<usize>>(&self, r: R) -> T {
            self.query_impl(0, &bound_to_range(r, 0..self.n), 0..self.n)
        }
        fn query_impl(&self, k: usize, r: &Range<usize>, a: Range<usize>) -> T {
            if !is_overlap(r, &a) {
                self.id.clone()
            } else if is_include(r, &a) {
                self.dat[k].clone()
            } else {
                let m = (a.start + a.end) / 2;
                (self.f)(
                    &self.query_impl(k * 2 + 1, r, a.start..m),
                    &self.query_impl(k * 2 + 2, r, m..a.end),
                )
            }
        }
    }
    pub struct LazySegTree<T, U, F, G, H> {
        n: usize,
        id: T,
        dat: Vec<T>,
        lazy: Vec<Option<U>>,
        f: F,
        g: G,
        h: H,
    }
    impl<T, U, F, G, H> LazySegTree<T, U, F, G, H>
    where
        T: Clone,
        U: Clone,
        F: Fn(&T, &T) -> T,
        G: Fn(&U, &U) -> U,
        H: Fn(&T, &U, usize) -> T,
    {
        pub fn new(n: usize, id: T, f: F, g: G, h: H) -> Self {
            let n = n.next_power_of_two();
            Self {
                n: n,
                id: id.clone(),
                dat: vec![id; 2 * n - 1],
                lazy: vec![None; 2 * n - 1],
                f: f,
                g: g,
                h: h,
            }
        }
        pub fn build_from_slice(dat: &[T], id: T, f: F, g: G, h: H) -> Self {
            let mut lst = Self::new(dat.len(), id, f, g, h);
            for i in 0..dat.len() {
                lst.dat[lst.n - 1 + i] = dat[i].clone();
            }
            for i in (0..lst.n - 1).rev() {
                lst.dat[i] = (lst.f)(&lst.dat[i * 2 + 1], &lst.dat[i * 2 + 2]);
            }
            lst
        }
        pub fn build_from_iter<I: Iterator<Item = T>>(iter: I, id: T, f: F, g: G, h: H) -> Self {
            use itertools::Itertools;
            Self::build_from_slice(&iter.collect_vec(), id, f, g, h)
        }
        pub fn len(&self) -> usize {
            self.n
        }
        fn marge_effect(&mut self, k: usize, x: &U) {
            if let Some(y) = self.lazy[k].take() {
                self.lazy[k] = Some((self.g)(&y, x));
            } else {
                self.lazy[k] = Some(x.clone());
            }
        }
        fn eval(&mut self, k: usize, l: usize) {
            if let Some(x) = self.lazy[k].take() {
                self.dat[k] = (self.h)(&self.dat[k], &x, l);
                if k < self.n - 1 {
                    self.marge_effect(k * 2 + 1, &x);
                    self.marge_effect(k * 2 + 2, &x);
                }
            }
        }
        pub fn update_range<R: RangeBounds<usize>>(&mut self, r: R, x: U) {
            self.update_range_impl(&bound_to_range(r, 0..self.n), 0, 0..self.n, &x)
        }
        fn update_range_impl(&mut self, r: &Range<usize>, k: usize, a: Range<usize>, x: &U) {
            if !is_overlap(r, &a) {
                self.eval(k, a.end - a.start);
                return;
            }
            if is_include(r, &a) {
                self.marge_effect(k, x);
                self.eval(k, a.end - a.start);
                return;
            }
            let m = (a.start + a.end) / 2;
            self.eval(k, a.end - a.start);
            self.update_range_impl(r, k * 2 + 1, a.start..m, x);
            self.update_range_impl(r, k * 2 + 2, m..a.end, x);
            self.dat[k] = (self.f)(&self.dat[k * 2 + 1], &self.dat[k * 2 + 2]);
        }
        pub fn query<R: RangeBounds<usize>>(&mut self, r: R) -> T {
            self.query_impl(&bound_to_range(r, 0..self.n), 0, 0..self.n)
        }
        fn query_impl(&mut self, r: &Range<usize>, k: usize, a: Range<usize>) -> T {
            if !is_overlap(r, &a) {
                self.id.clone()
            } else if is_include(r, &a) {
                self.eval(k, a.end - a.start);
                self.dat[k].clone()
            } else {
                let m = (a.start + a.end) / 2;
                self.eval(k, a.end - a.start);
                let x = self.query_impl(r, k * 2 + 1, a.start..m);
                let y = self.query_impl(r, k * 2 + 2, m..a.end);
                (self.f)(&x, &y)
            }
        }
    }
}


Measured memory_bytes: 59908