「ZJOI2017」树状数组 题解

前言

题目链接:洛谷UOJLOJ

UOJ 上有很强的数据。

题意简述

yzh 做 OI 题维护序列 \(\{a_n\}\)

她实现了一个后缀和查询函数 \(\displaystyle f(x) = \begin{cases} 0 & \text{ if } x=0 \\ \sum\limits_{i=x}^n a_i & \text{ otherwise } \end{cases}\),和一个区间查询函数 \(g(l, r) = f(r) - f(l - 1)\),显然,她并没有意识到这里有些小问题。以上运算都是在 \(\mathbb{F}_2\) 意义下进行的。

初始 \(a_i = 0\)。这道问题有 \(m\) 次操作,每种操作类型为单点加一、区间查询。单点加参数是一个区间 \([l_i, r_i]\),表示随机选取 \(p\in[l_i, r_i]\),让 \(a_p\gets a_p+1\);区间查询是正常的区间查询和在 \(\mathbb{F}_2\) 意义下的结果。

现在,你需要对每一个查询,求出她错误的实现依然能够得到正确答案的概率,对 \(998244353\) 取模。

\(n,m\leq10^5\)

题目分析

初步分析

肯定要分析 \(g(l, r)\) 什么时候正确。不妨对 \(l = 1\) 与否进行讨论。

  1. \(l = 1\)
    此时 \(f(l - 1) = f(0) = 0\)

    \[\begin{aligned} g(l, r) &= \sum\limits_{i=l}^r a_i \\ \Leftrightarrow\quad \sum\limits_{i=r}^n a_i &= \sum\limits_{i=l}^r a_i \\ \Leftrightarrow\quad \sum\limits_{i=r+1}^n a_i &= \sum\limits_{i=l}^{r-1} a_i \\ \Leftrightarrow\quad \sum\limits_{i=1}^n a_i - \sum\limits_{i=1}^r a_i &= \sum\limits_{i=l}^{r-1} a_i \\ \Leftrightarrow\quad \sum\limits_{i=1}^n a_i - \sum\limits_{i=1}^{r-1} a_i - a_r &= \sum\limits_{i=l}^{r-1} a_i \\ \Leftrightarrow\quad \sum\limits_{i=1}^n a_i - a_r &= 2\sum\limits_{i=l}^{r-1} a_i \\ \end{aligned} \]

    注意到在模 \(2\) 意义下,右侧为 \(0\)

    \[\begin{aligned} \Leftrightarrow\quad \sum\limits_{i=1}^n a_i=a_r \end{aligned} \]

    左侧就是到目前位置我们进行了多少次操作,这个随便维护。我们就把问题具象化成求某一个位置为 \(0/1\) 的概率。
  2. \(l > 1\)

    \[\begin{aligned} g(l, r) &= \sum\limits_{i=l}^r a_i \\ \Leftrightarrow\quad \sum\limits_{i=r}^n a_i-\sum\limits_{i=l-1}^n a_i &= \sum\limits_{i=l}^r a_i \\ \Leftrightarrow\quad \sum\limits_{i=l}^r a_i + \sum\limits_{i=l-1}^{r-1}a_i &= 0 \\ \Leftrightarrow\quad a_{l-1}+a_{r} + 2\sum\limits_{i=l}^{r-1}a_i &= 0 \\ \Leftrightarrow\quad a_{l-1}+a_{r} &= 0 \\ \Leftrightarrow\quad a_{l-1} &= a_{r} \\ \end{aligned} \]

    问题简化成求两个位置值相等的概率。

暴力模拟

我们考虑一个 \(\mathcal{O}(nm)\) 的算法。

对于第一个问题(求 \(a_i=x\) 的概率),我们考虑维护 \(f[0/1]\) 表示 \(a_i=0/1\) 的概率,初始 \(f[0] = 1, f[1] = 0\)

\(\mathcal{O}(m)\) 地枚举一个包含 \(i\) 的修改 \([l, r]\ni i\),那么它恰好修改到 \(a_i\) 的概率为 \(p=\dfrac{1}{r-l+1}\)。也就是说,\(a_i\)\(p\) 的概率变化,\(1-p\) 的概率不变化。那么 \(f'[o] \gets p\cdot f[\neg o] + (1-p)f[o]\)。于是解决了该问题。

不怎么有用的发现

事实上,我们只维护 \(f[0] / f[1]\) 即可,因为 \(f[0]+f[1] \equiv 1\)

对于第二个问题,我们想当然地求出 \(f_{l-1}[0/1], f_r[0/1]\),答案为 \(f_{l-1}[0]f_r[0] + f_{l-1}[1]f_r[1]\)。但是,这是错误的。

我们发现,有些操作对 \(a_{l-1}, a_r\) 的影响不是独立的。对于一个修改 \([ql, qr]\supseteq[l-1,r]\),那么倘若随机到了 \(a_{l-1}\),那么此时 \(a_r\) 必不可能被修改。这看上去似乎很棘手,事实上,我们仅需将状态扩充至 \(f[0/1][0/1]\) 表示 \(a_{l-1}, a_r\) 的值即可。这样,对于完全包含 \([l-1, r]\) 的转移为 \(f'[x][y]\gets p\cdot f[\neg x][y]+p\cdot f[x][\neg y] + (1-2p)f[x][y]\)。剩下仅包含一个端点的转移类似第一个问题。初值 \(f[0][0]=1\),其他为 \(0\)

namespace $pts50 {

pair<int, int> Q[N];

void modify(int l, int r) {
	Q[++X] = { l, r };
}

mint query(int l, int r) {
	if (l > 1) {
		--l;
		mint a[2][2] = {
			{ 1, 0 },
			{ 0, 0 }
		};
		for (int i = 1; i <= X; ++i) {
			mint t[2][2] = {{ a[0][0], a[0][1] }, { a[1][0], a[1][1] }};
			mint p = inv[Q[i].second - Q[i].first + 1];
			if (Q[i].first <= l && r <= Q[i].second) {
				for (int x : { 0, 1 })
					for (int y : { 0, 1 })
						a[x][y] = t[!x][y] * p + t[x][!y] * p + t[x][y] * (1 - p * 2);
			} else if (Q[i].first <= l && l <= Q[i].second) {
				for (int o : { 0, 1 }) {
					a[0][o] = t[0][o] * (1 - p) + t[1][o] * p;
					a[1][o] = t[1][o] * (1 - p) + t[0][o] * p;
				}
			} else if (Q[i].first <= r && r <= Q[i].second) {
				for (int o : { 0, 1 }) {
					a[o][0] = t[o][0] * (1 - p) + t[o][1] * p;
					a[o][1] = t[o][1] * (1 - p) + t[o][0] * p;
				}
			}
		}
		return a[0][0] + a[1][1];
	} else {
		mint a[] = { 1, 0 };
		for (int i = 1; i <= X; ++i)
			if (Q[i].first <= r && r <= Q[i].second) {
				mint p = inv[Q[i].second - Q[i].first + 1];
				mint t[] = { a[0], a[1] };
				a[0] = t[0] * (1 - p) + t[1] * p;
				a[1] = t[1] * (1 - p) + t[0] * p;
			}
		return a[X & 1];
	}
}

void solve() {
	for (int op, l, r, i = 1; i <= m; ++i) {
		scanf("%d%d%d", &op, &l, &r);
		if (op == 1) {
			modify(l, r);
		} else {
			printf("%d\n", query(l, r).raw());
		}
	}
}

}

抽丝剥茧,转化为熟悉的模型

问题都分析到这了,矩阵呼之欲出了。各种数据结构在你脑海里的形象愈发清晰。

问题一

先来看看好下手的问题一。我们把 \(f[0/1]\) 看做一个 \(1\times 2\) 的矩阵,那么对于一个修改,就可以构造出转移矩阵:

\[\begin{bmatrix} \small{f[0]} & \small{f[1]} \end{bmatrix} \times \begin{bmatrix} 1-p & p \\ p & 1-p \end{bmatrix} = \begin{bmatrix} \small{f'[0]} & \small{f'[1]} \end{bmatrix} \]

然后,我们请出一个能够区间乘矩阵,单点查询的数据结构。线段树是也。

于是,我们在线地解决了问题一,修改 / 询问 \(\mathcal{O}(w^3\log n)\),其中 \(w=2\)

问题二

类似的,我们把 \(f[0/1][0/1]\) 拍扁成一个 \(1\times 4\) 的矩阵,并构造出转移矩阵。

  1. 完全包含

\[\begin{bmatrix} \scriptsize{f[0][0]} & \scriptsize{f[0][1]} & \scriptsize{f[1][0]} & \scriptsize{f[1][1]} \end{bmatrix} \times \begin{bmatrix} 1-2p&p&p& \\ p&1-2p& &p \\ p& &1-2p&p \\ &p&p&1-2p \end{bmatrix} = \begin{bmatrix} \scriptsize{f'[0][0]} & \scriptsize{f'[0][1]} & \scriptsize{f'[1][0]} & \scriptsize{f'[1][1]} \end{bmatrix} \]

  1. 仅包含左端点

\[\begin{bmatrix} \scriptsize{f[0][0]} & \scriptsize{f[0][1]} & \scriptsize{f[1][0]} & \scriptsize{f[1][1]} \end{bmatrix} \times \begin{bmatrix} 1-p&&p& \\ &1-p&& \\ p&&1-p&p \\ &p&&1-p \end{bmatrix} = \begin{bmatrix} \scriptsize{f'[0][0]} & \scriptsize{f'[0][1]} & \scriptsize{f'[1][0]} & \scriptsize{f'[1][1]} \end{bmatrix} \]

  1. 仅包含右端点

\[\begin{bmatrix} \scriptsize{f[0][0]} & \scriptsize{f[0][1]} & \scriptsize{f[1][0]} & \scriptsize{f[1][1]} \end{bmatrix} \times \begin{bmatrix} 1-p&p&& \\ p&1-p&& \\ &&1-p&p \\ &&p&1-p \end{bmatrix} = \begin{bmatrix} \scriptsize{f'[0][0]} & \scriptsize{f'[0][1]} & \scriptsize{f'[1][0]} & \scriptsize{f'[1][1]} \end{bmatrix} \]

但是,这时候,我们发现有了区间包含关系的限制,十分地不好搞。

发现如果拍到一个二维笛卡尔坐标系上,这是二维数点状物。俗话说得好,二维数点,静态扫描线而动态 CDQ 也。这里我们使用 CDQ 分治就能解决问题。

三维偏序,一维时间,时间轴分治解决;再一维双指针维护解决;最后一维,数据结构解决。

我们先递归解决 \(L=[l, mid], R=(mid, r]\),然后考虑 \(L\) 中的修改对 \(R\) 中的询问产生的影响。

这里我们先递归了 \(R\),而不是递归 \(L\),然后解决跨过分治中心,再递归 \(R\),是因为修改的顺序并不影响答案,所以是对的。事实上,严格按照修改的顺序分治也可以。

考虑完全包含、包含左端点两种情况,包含右端点类似。

我们先分别把 \(L, R\) 里所有操作按照左端点升序排序。

考虑以此枚举 \(i\in R\),同时双指针维护所有左端点小于等于 \(i\) 的左端点的 \(j\)

上图中,中间灰色的线表示分治中心,右侧黑色的竖线表示我们枚举的 \(i\)。我们可以把左侧的 \(j\) 分为三类。

  • 红色:这一类右端点已经小于 \(i\) 的左端点,没有用处,忽略。
  • 蓝色:这一类右端点大于等于 \(i\) 的右端点,是完全包含类型。
  • 绿色:这一类右端点小于 \(i\) 的右端点,仅包含 \(i\) 的左端点。

所以我们情不自禁使用两个数据结构,分别维护两种转移矩阵。查询的时候,按照需要查询即可。这个数据结构需要支持单点修改,区间查询矩阵乘。动态开点线段树即可。

于是,我们离线地解决了问题二。时间复杂度不难分析出为 \(\mathcal{O}\Big(w^3 m\log m(\log m+\log n)\Big)\),其中 \(w = 4\)

收尾工作(卡常

整体空间复杂度很不错,差不多是线性的 \(\mathcal{O}(w^2 (n + m))\)

整体时间复杂度在两只 \(\log\) 的基础上有矩阵乘法 \(w^3\) 的加持,十分难受。

我们发现,由于我们提到修改的顺序不影响答案,那么我们把处理右端点提出来,做两次 CDQ 分治,这样,每次分治内部不需要排序,而是使用原地归并。将 \((\log m+\log n)\) 因子优化为了 \(\log n\) 因子。

我们再次发现,在处理完全包含类型时,我们的区间查询是后缀查询,那么用一棵树状数组代替一棵线段树,时间复杂度没有变化,但是常数更小了。由于我不会动态开点树状数组这种科技,所以采用了时间戳清空。

我们又又发现,线段树可以搞一搞。我们多维护一个 bool 类型的标记,表示这个节点的矩阵是不是单位矩阵,在询问时,如果遇到一个单位矩阵,就不进行矩阵乘法。在新建节点时,我们采用直接赋值,而不是先弄成单位矩阵,再做一次矩阵乘法。

我们努努力,把矩阵乘法手动做循环展开。

我们上传统手艺,加上超级快读快写。

我们发现,我们终于过了这题……

代码

原味代码($920$ 行)
#pragma GCC optimize("Ofast", "inline", "fast-math")

#include <cstdio>
#include <iostream>
#include <limits>
#include <cassert>
#include <cstring>
#include <algorithm>
using namespace std;

namespace Mod_Int_Class {
    template <typename T, typename _Tp>
    constexpr bool in_range(_Tp val) {
        return std::numeric_limits<T>::min() <= val && val <= std::numeric_limits<T>::max();
    }
    
    template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
    static constexpr inline bool is_prime(_Tp val) {
        if (val < 2) return false;
        for (_Tp i = 2; i * i <= val; ++i)
            if (val % i == 0)
                return false;
        return true;
    }
    
    template <auto _mod = 998244353, typename T = int, typename S = long long>
    class Mod_Int {
        static_assert(in_range<T>(_mod), "mod must in the range of type T.");
        static_assert(std::is_integral<T>::value, "type T must be an integer.");
        static_assert(std::is_integral<S>::value, "type S must be an integer.");
	public:
		constexpr Mod_Int() noexcept = default;
		template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
		constexpr Mod_Int(_Tp v) noexcept: val(0) {
			if (0 <= S(v) && S(v) < mod) val = v;
			else val = (S(v) % mod + mod) % mod;
		}
		
		constexpr T const& raw() const {
			return this -> val;
		}
		static constexpr T mod = _mod;
		
		template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
		constexpr friend Mod_Int pow(Mod_Int a, _Tp p) {
			return a ^ p;
		}
		constexpr friend Mod_Int sub(Mod_Int a, Mod_Int b) {
			return a - b;
		}
		constexpr friend Mod_Int& tosub(Mod_Int& a, Mod_Int b) {
			return a -= b;
		}
		
		constexpr friend Mod_Int add(Mod_Int a) { return a; }
		template <typename... args_t>
		constexpr friend Mod_Int add(Mod_Int a, args_t... args) {
			return a + add(args...);
		}
		constexpr friend Mod_Int mul(Mod_Int a) { return a; }
		template <typename... args_t>
		constexpr friend Mod_Int mul(Mod_Int a, args_t... args) {
			return a * mul(args...);
		}
		template <typename... args_t>
		constexpr friend Mod_Int& toadd(Mod_Int& a, args_t... b) {
			return a = add(a, b...);
		}
		template <typename... args_t>
		constexpr friend Mod_Int& tomul(Mod_Int& a, args_t... b) {
			return a = mul(a, b...);
		}
		
		template <T __mod = mod, typename = std::enable_if_t<is_prime(__mod)>>
		static constexpr inline T inv(T a) {
			assert(a != 0);
			return _pow(a, mod - 2);
		}
		
		constexpr Mod_Int& operator + () const {
			return *this;
		}
		constexpr Mod_Int operator - () const {
			return _sub(0, val);
		}
		constexpr Mod_Int inv() const {
			return inv(val);
		}
		
		constexpr friend inline Mod_Int operator + (Mod_Int a, Mod_Int b) {
			return _add(a.val, b.val);
		}
		constexpr friend inline Mod_Int operator - (Mod_Int a, Mod_Int b) {
			return _sub(a.val, b.val);
		}
		constexpr friend inline Mod_Int operator * (Mod_Int a, Mod_Int b) {
			return _mul(a.val, b.val);
		}
		constexpr friend inline Mod_Int operator / (Mod_Int a, Mod_Int b) {
			return _mul(a.val, inv(b.val));
		}
		template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
		constexpr friend inline Mod_Int operator ^ (Mod_Int a, _Tp p) {
			return _pow(a.val, p);
		}
		
		constexpr friend inline Mod_Int& operator += (Mod_Int& a, Mod_Int b) {
			return a = _add(a.val, b.val);
		}
		constexpr friend inline Mod_Int& operator -= (Mod_Int& a, Mod_Int b) {
			return a = _sub(a.val, b.val);
		}
		constexpr friend inline Mod_Int& operator *= (Mod_Int& a, Mod_Int b) {
			return a = _mul(a.val, b.val);
		}
		constexpr friend inline Mod_Int& operator /= (Mod_Int& a, Mod_Int b) {
			return a = _mul(a.val, inv(b.val));
		}
		template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
		constexpr friend inline Mod_Int& operator ^= (Mod_Int& a, _Tp p) {
			return a = _pow(a.val, p);
		}
		
		constexpr friend inline bool operator == (Mod_Int a, Mod_Int b) {
			return a.val == b.val;
		}
		constexpr friend inline bool operator != (Mod_Int a, Mod_Int b) {
			return a.val != b.val;
		}
		
		constexpr Mod_Int& operator ++ () {
			this -> val + 1 == mod ? this -> val = 0 : ++this -> val;
			return *this;
		}
		constexpr Mod_Int& operator -- () {
			this -> val == 0 ? this -> val = mod - 1 : --this -> val;
			return *this;
		}
		constexpr Mod_Int operator ++ (int) {
			Mod_Int res = *this;
			this -> val + 1 == mod ? this -> val = 0 : ++this -> val;
			return res;
		}
		constexpr Mod_Int operator -- (int) {
			Mod_Int res = *this;
			this -> val == 0 ? this -> val = mod - 1 : --this -> val;
			return res;
		}
		
		friend std::istream& operator >> (std::istream& is, Mod_Int<mod, T, S>& x) {
			T ipt;
			return is >> ipt, x = ipt, is;
		}
		friend std::ostream& operator << (std::ostream& os, Mod_Int<mod, T, S> x) {
			return os << x.val;
		}
	protected:
		T val;
		
		static constexpr inline T _add(T a, T b) {
			return a >= mod - b ? a + b - mod : a + b;
		}
		static constexpr inline T _sub(T a, T b) {
			return a < b ? a - b + mod : a - b;
		}
		static constexpr inline T _mul(T a, T b) {
			return static_cast<S>(a) * b % mod;
		}
		
		template <typename _Tp, typename = std::enable_if_t<std::is_integral<_Tp>::value>>
		static constexpr inline T _pow(T a, _Tp p) {
			T res = 1;
			for (; p; p >>= 1, a = _mul(a, a))
				if (p & 1) res = _mul(res, a);
			return res;
		}
    };
	
    using mint = Mod_Int<>;
	using mod_t = mint;
	
    constexpr mint operator ""_m (unsigned long long x) {
        return mint(x);
    }
    constexpr mint operator ""_mod (unsigned long long x) {
        return mint(x);
    }
}

using namespace Mod_Int_Class;

namespace FASTIO {

const int MAX = 1 << 26;
char buf[MAX], *ip = buf, obuf[MAX], *op = obuf;
#define putchar(x) *FASTIO::op++ = x
template <typename T>
inline void read(T &x) {
    x = 0; char ch = *ip++;
    for (; ch <  48; ch = *ip++);
    for (; ch >= 48; ch = *ip++) x = (x << 3) + (x << 1) + (ch ^ 48);
}
template <typename T>
inline void write(T x) {
    static short stack[20], top(0);
    do stack[++top] = x % 10; while (x /= 10);
    while (top) putchar(stack[top--] | 48);
}

}

char MST;

const int N = 100010;

int n, m, X;
mint inv[N];

#if false

namespace $pts50 {

pair<int, int> Q[N];

void modify(int l, int r) {
	Q[++X] = { l, r };
}

mint query(int l, int r) {
	if (l > 1) {
		--l;
		mint a[2][2] = {
			{ 1, 0 },
			{ 0, 0 }
		};
		for (int i = 1; i <= X; ++i) {
			mint t[2][2] = {{ a[0][0], a[0][1] }, { a[1][0], a[1][1] }};
			mint p = inv[Q[i].second - Q[i].first + 1];
			if (Q[i].first <= l && r <= Q[i].second) {
				for (int x : { 0, 1 })
					for (int y : { 0, 1 })
						a[x][y] = t[!x][y] * p + t[x][!y] * p + t[x][y] * (1 - p * 2);
			} else if (Q[i].first <= l && l <= Q[i].second) {
				for (int o : { 0, 1 }) {
					a[0][o] = t[0][o] * (1 - p) + t[1][o] * p;
					a[1][o] = t[1][o] * (1 - p) + t[0][o] * p;
				}
			} else if (Q[i].first <= r && r <= Q[i].second) {
				for (int o : { 0, 1 }) {
					a[o][0] = t[o][0] * (1 - p) + t[o][1] * p;
					a[o][1] = t[o][1] * (1 - p) + t[o][0] * p;
				}
			}
		}
		return a[0][0] + a[1][1];
	} else {
		mint a[] = { 1, 0 };
		for (int i = 1; i <= X; ++i)
			if (Q[i].first <= r && r <= Q[i].second) {
				mint p = inv[Q[i].second - Q[i].first + 1];
				mint t[] = { a[0], a[1] };
				a[0] = t[0] * (1 - p) + t[1] * p;
				a[1] = t[1] * (1 - p) + t[0] * p;
			}
		return a[X & 1];
	}
}

void solve() {
	for (int op, l, r, i = 1; i <= m; ++i) {
		scanf("%d%d%d", &op, &l, &r);
		if (op == 1) {
			modify(l, r);
		} else {
			printf("%d\n", query(l, r).raw());
		}
	}
}

}

#endif

namespace $no_DS {

#if false

template <size_t O>
struct Matrix_O {
	mint a[O][O];
	int n, m;
	
	void init(int _n, int _m) {
		n = _n, m = _m;
		memset(a, 0x00, sizeof(a));
	}
	
	void unit() {
		assert(n == m);
		for (int i = 0; i < n; ++i)
			a[i][i] = 1;
	}
	
	mint * operator [] (int x) {
		return a[x];
	}
	
	mint const * operator [] (int x) const {
		return a[x];
	}
	
	friend Matrix_O operator * (const Matrix_O& a, const Matrix_O& b) {
		Matrix_O res;
		assert(a.m == b.n);
		res.init(a.n, b.m);
		for (int i = 0; i < a.n; ++i)
			for (int j = 0; j < b.m; ++j)
				for (int k = 0; k < a.m; ++k)
					res[i][j] += a[i][k] * b[k][j];
		return res;
	}
	
	friend Matrix_O& operator *= (Matrix_O& a, const Matrix_O& b) {
		return a = a * b;
	}
	
	void output() {
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < m; ++j)
				fprintf(stderr, "%d ", a[i][j].raw());
			fprintf(stderr, "\n");
		}
	}
};

#endif

mint ans[N];

namespace sub1 {

struct Matrix {
	mint a[4][4];
	
	inline void init() {
		memset(a, 0x00, sizeof(a));
	}
	
	inline void unit() {
		a[0][0] = 1;
		a[1][1] = 1;
		a[2][2] = 1;
		a[3][3] = 1;
	}
	
	mint * operator [] (int x) {
		return a[x];
	}
	
	mint const * operator [] (int x) const {
		return a[x];
	}
	
	friend Matrix operator * (const Matrix& a, const Matrix& b) {
		Matrix res;
		res[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0] + a[0][3] * b[3][0];
		res[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1] + a[0][3] * b[3][1];
		res[0][2] = a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2] + a[0][3] * b[3][2];
		res[0][3] = a[0][0] * b[0][3] + a[0][1] * b[1][3] + a[0][2] * b[2][3] + a[0][3] * b[3][3];
		res[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0] + a[1][3] * b[3][0];
		res[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1] + a[1][3] * b[3][1];
		res[1][2] = a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2] + a[1][3] * b[3][2];
		res[1][3] = a[1][0] * b[0][3] + a[1][1] * b[1][3] + a[1][2] * b[2][3] + a[1][3] * b[3][3];
		res[2][0] = a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0] + a[2][3] * b[3][0];
		res[2][1] = a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1] + a[2][3] * b[3][1];
		res[2][2] = a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2] + a[2][3] * b[3][2];
		res[2][3] = a[2][0] * b[0][3] + a[2][1] * b[1][3] + a[2][2] * b[2][3] + a[2][3] * b[3][3];
		res[3][0] = a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + a[3][3] * b[3][0];
		res[3][1] = a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + a[3][3] * b[3][1];
		res[3][2] = a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + a[3][3] * b[3][2];
		res[3][3] = a[3][0] * b[0][3] + a[3][1] * b[1][3] + a[3][2] * b[2][3] + a[3][3] * b[3][3];
		return res;
	}
	
	friend Matrix& operator *= (Matrix& a, const Matrix& b) {
		return a = a * b;
	}
};

// using Matrix = Matrix_O<4>;

struct Segment_Tree {
	Matrix a[N << 1];
	int ls[N << 1], rs[N << 1];
	bool tag[N << 1];
	int pcnt, rt;
	
	inline int newNode() {
		a[++pcnt].init();
		a[pcnt].unit();
		ls[pcnt] = rs[pcnt] = 0;
		tag[pcnt] = false;
		return pcnt;
	}
	
	void modify(int& idx, int trl, int trr, int p, const Matrix& x) {
		if (!idx) idx = newNode();
		if (tag[idx])
			a[idx] *= x;
		else
			a[idx] = x, tag[idx] = true;
		if (trl == trr) return;
		int mid = (trl + trr) >> 1;
		if (p <= mid)
			modify(ls[idx], trl, mid, p, x);
		else
			modify(rs[idx], mid + 1, trr, p, x);
	}
	
	inline void modify(int p, const Matrix& x) {
		modify(rt, 1, n, p, x);
	}
	
	inline void build() {
		pcnt = 0, rt = 0;
		// a[0].init(K, K), a[0].unit();
	}
	
	void query(int idx, int trl, int trr, int l, int r, Matrix& x) {
		if (l <= trl && trr <= r) {
			if (tag[idx])
				x *= a[idx];
			return;
		}
		int mid = (trl + trr) >> 1;
		if (ls[idx] && l <= mid)
			query(ls[idx], trl, mid, l, r, x);
		if (rs[idx] && r > mid)
			query(rs[idx], mid + 1, trr, l, r, x);
	}
	
	inline Matrix query(int l, int r) {
		Matrix t;
		t.init();
		t.unit();
		if (rt)
			query(rt, 1, n, l, r, t);
		return t;
	}
	
	// Matrix a[N];
	// int K;
	
	// void modify(int p, const Matrix& x) {
	// 	a[p] *= x;
	// }
	
	// void build(int k) {
	// 	K = k;
	// 	for (int i = 1; i <= n; ++i)
	// 		a[i].init(k, k), a[i].unit();
	// }
	
	// Matrix query(int l, int r) {
	// 	Matrix res;
	// 	res.init(K, K), res.unit();
	// 	for (int i = l; i <= r; ++i)
	// 		res *= a[i];
	// 	return res;
	// }
};

struct Bit_Tree {
	Matrix a[N];
	int T[N], timer;
	
	inline void refresh(int x) {
		if (T[x] != timer) {
			T[x] = timer;
			a[x].init();
			a[x].unit();
		}
	}
	
	inline void modify(int p, const Matrix& x) {
		for (; p; p &= p - 1)
			if (T[p] == timer) a[p] *= x;
			else T[p] = timer, a[p] = x;
			// refresh(p), a[p] *= x;
	}
	
	inline void build() {
		++timer;
	}
	
	inline Matrix querysuf(int p) const {
		Matrix t;
		t.init();
		t.unit();
		for (; p <= n; p += p & -p)
			if (T[p] == timer)
				t *= a[p];
		return t;
	}
};

struct Question {
	int idx, l, r, T;
} q[N];
Matrix qv[N];

int qcnt;

void init() {
	
}

inline void add1(int l, int r) {
	q[++qcnt] = { -1, l, r, 0 };
    q[qcnt].T = qcnt;
}

inline void add2(int l, int r, int idx) {
	assert(l > 1);
	--l;
	q[++qcnt] = { idx, l, r, 0 };
    q[qcnt].T = qcnt;
	qv[idx].init();
	qv[idx].unit();
}

Bit_Tree t1;
Segment_Tree t2, t3;

// inline int f(bool x, bool y) {
// 	return x << 1 | y;
// }

void solve(int l, int r) {
	if (l == r) return;
	int mid = (l + r) >> 1;
	solve(l, mid), solve(mid + 1, r);
	
	// static auto f = [] (int x, int y) -> int {
	// 	return x << 1 | y;
	// };
	
	// 完全包含和包含左端点
	// sort(q + l, q + mid + 1,
	// 	[] (const Question& a, const Question& b) -> bool {
	// 		return a.l < b.l;
	// 	}
	// );
	// sort(q + mid + 1, q + r + 1,
	// 	[] (const Question& a, const Question& b) -> bool {
	// 		return a.l < b.l;
	// 	}
	// );
	t1.build(), t2.build();
	for (int i = mid + 1, j = l; i <= r; ++i) {
		for (; j <= mid && q[j].l <= q[i].l; ++j)
			if (!~q[j].idx) {
				int x = q[j].r;
				mint p = inv[q[j].r - q[j].l + 1];
				Matrix t;
				
				// 完全包含
				t.init();
				t[0b10][0b00] = p;
				t[0b01][0b00] = p;
				t[0b00][0b00] = 1 - p * 2;
				t[0b11][0b01] = p;
				t[0b00][0b01] = p;
				t[0b01][0b01] = 1 - p * 2;
				t[0b00][0b10] = p;
				t[0b11][0b10] = p;
				t[0b10][0b10] = 1 - p * 2;
				t[0b01][0b11] = p;
				t[0b10][0b11] = p;
				t[0b11][0b11] = 1 - p * 2;
				
				// for (bool x : { 0, 1 })
				// 	for (bool y : { 0, 1 }) {
				// 		t[f(!x, y)][f(x, y)] += p;
				// 		t[f(x, !y)][f(x, y)] += p;
				// 		t[f(x, y)][f(x, y)] += 1 - p * 2;
				// 	}
				t1.modify(x, t);
				
				// 左端点
				t.init();
				t[0b00][0b00] = 1 - p;
				t[0b10][0b00] = p;
				t[0b10][0b10] = 1 - p;
				t[0b00][0b10] = p;
				t[0b01][0b01] = 1 - p;
				t[0b11][0b01] = p;
				t[0b11][0b11] = 1 - p;
				t[0b01][0b11] = p;
				
				// for (bool o : { 0, 1 }) {
				// 	t[f(0, o)][f(0, o)] += 1 - p;
				// 	t[f(1, o)][f(0, o)] += p;
				// 	t[f(1, o)][f(1, o)] += 1 - p;
				// 	t[f(0, o)][f(1, o)] += p;
				// }
				t2.modify(x, t);
			}
		if (~q[i].idx) {
			qv[q[i].idx] *= t1.querysuf(q[i].r);
			if (q[i].l <= q[i].r - 1)
				qv[q[i].idx] *= t2.query(q[i].l, q[i].r - 1);
		}
	}
	
	inplace_merge(q + l, q + mid + 1, q + r + 1,
		[] (const Question& a, const Question& b) -> bool {
			return a.l < b.l;
		}
	);
}

void solve2(int l, int r) {
	if (l == r) return;
	int mid = (l + r) >> 1;
	solve2(l, mid), solve2(mid + 1, r);
    
    t3.build();
	for (int i = r, j = mid; i >= mid + 1; --i) {
		for (; j >= l && q[j].r >= q[i].r; --j)
			if (!~q[j].idx) {
				int x = q[j].l;
				mint p = inv[q[j].r - q[j].l + 1];
				Matrix t;
				
				t.init();
				t[0b00][0b00] = 1 - p;
				t[0b01][0b00] = p;
				t[0b01][0b01] = 1 - p;
				t[0b00][0b01] = p;
				t[0b10][0b10] = 1 - p;
				t[0b11][0b10] = p;
				t[0b11][0b11] = 1 - p;
				t[0b10][0b11] = p;
				
				// for (bool o : { 0, 1 }) {
				// 	t[f(o, 0)][f(o, 0)] += 1 - p;
				// 	t[f(o, 1)][f(o, 0)] += p;
				// 	t[f(o, 1)][f(o, 1)] += 1 - p;
				// 	t[f(o, 0)][f(o, 1)] += p;
				// }
				t3.modify(x, t);
			}
		if (~q[i].idx) {
			if (q[i].l + 1 <= q[i].r)
				qv[q[i].idx] *= t3.query(q[i].l + 1, q[i].r);
		}
	}
    
    inplace_merge(q + l, q + mid + 1, q + r + 1,
		[] (const Question& a, const Question& b) -> bool {
			return a.r < b.r;
		}
	);
}

void solve() {
	solve(1, qcnt);
    sort(q + 1, q + qcnt + 1, [] (const Question& a, const Question& b) -> bool {
        return a.T < b.T;
    });
    solve2(1, qcnt);
	
	for (int l = 1; l <= qcnt; ++l)
		if (~q[l].idx) {
			Matrix t;
			t.init();
			t[0][0] = 1;
			t *= qv[q[l].idx];
			ans[q[l].idx] = t[0][0] + t[0][3];
		}
}

}

namespace sub2 {

struct Matrix {
	mint a[2][2];
	
	inline void init() {
		memset(a, 0x00, sizeof(a));
	}
	
	inline void unit() {
		a[0][0] = 1;
		a[1][1] = 1;
	}
	
	mint * operator [] (int x) {
		return a[x];
	}
	
	mint const * operator [] (int x) const {
		return a[x];
	}
	
	friend Matrix operator * (const Matrix& a, const Matrix& b) {
		Matrix res;
		res[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0];
		res[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1];
		res[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0];
		res[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1];
		return res;
	}
	
	friend Matrix& operator *= (Matrix& a, const Matrix& b) {
		return a = a * b;
	}
};

struct Segment_Tree {
	Matrix a[N << 1];
	int ls[N << 1], rs[N << 1];
	bool tag[N << 1];
	int pcnt, rt;
	
	inline int newNode() {
		a[++pcnt].init();
		a[pcnt].unit();
		ls[pcnt] = rs[pcnt] = 0;
		tag[pcnt] = false;
		return pcnt;
	}
	
	void modify(int& idx, int trl, int trr, int l, int r, const Matrix& x) {
		if (!idx) idx = newNode();
		if (l <= trl && trr <= r) {
			if (tag[idx])
				a[idx] *= x;
			else
				a[idx] = x, tag[idx] = true;
			return;
		}
		int mid = (trl + trr) >> 1;
		if (l <= mid) modify(ls[idx], trl, mid, l, r, x);
		if (r >  mid) modify(rs[idx], mid + 1, trr, l, r, x);
	}
	
	inline void modify(int l, int r, const Matrix& x) {
		modify(rt, 1, n, l, r, x);
	}
	
	inline void build() {
		pcnt = 0, rt = 0;
	}
	
	void query(int idx, int trl, int trr, int p, Matrix& x) {
		if (tag[idx])
			x *= a[idx];
		if (trl == trr) return;
		int mid = (trl + trr) >> 1;
		if (ls[idx] && p <= mid)
			query(ls[idx], trl, mid, p, x);
		else if (rs[idx] && p > mid)
			query(rs[idx], mid + 1, trr, p, x);
	}
	
	inline Matrix query(int p) {
		Matrix t;
		t.init();
		t.unit();
		if (rt)
			query(rt, 1, n, p, t);
		return t;
	}
	
	// Matrix a[N];
	// int K;
	
	// void modify(int l, int r, const Matrix& x) {
	// 	for (int i = l; i <= r; ++i)
	// 		a[i] *= x;
	// }
	
	// void build(int k) {
	// 	K = k;
	// 	for (int i = 1; i <= n; ++i)
	// 		a[i].init(k, k), a[i].unit();
	// }
	
	// Matrix query(int p) {
	// 	return a[p];
	// }
};
	
Segment_Tree yzh;
int X;

inline void init() {
	yzh.build(), X = 0;
}

void add1(int l, int r) {
	Matrix t;
	t.init();
	mint p = inv[r - l + 1];
	t[0][0] = 1 - p, t[0][1] = p;
	t[1][0] = p, t[1][1] = 1 - p;
	yzh.modify(l, r, t);
	++X;
}

void add2(int x, int idx) {
	Matrix res = yzh.query(x);
	Matrix t;
	t.init();
	t[0][0] = 1, t[0][1] = 0;
	t = t * res;
	ans[idx] = t[0][X & 1];
}

void solve() {
	
}

}

bool isQ[N];

void solve() {
	sub1::init(), sub2::init();
	for (int op, l, r, i = 1; i <= m; ++i) {
		FASTIO::read(op);
		FASTIO::read(l), FASTIO::read(r);
		// scanf("%d%d%d", &op, &l, &r);
		if (op == 1) {
			sub1::add1(l, r);
			sub2::add1(l, r);
		} else {
			isQ[i] = true;
			if (l > 1) {
				sub1::add2(l, r, i);
			} else {
				sub2::add2(r, i);
			}
		}
	}
	sub1::solve(), sub2::solve();
	for (int i = 1; i <= m; ++i)
		if (isQ[i])
			FASTIO::write(ans[i].raw()), putchar('\n');
}

}

char MED;

int main() {
	fprintf(stderr, "Memory: %.2lfMB\n", (&MED - &MST) / 1024. / 1024);
	
	// freopen("ex_bit2.in", "r", stdin);
	// freopen("yzh", "w", stdout);
#ifndef XuYueming
	// freopen("bit.in", "r", stdin);
	// freopen("bit.out", "w", stdout);
#endif
	fread(FASTIO::buf, 1, FASTIO::MAX, stdin);
    // scanf("%d%d", &n, &m);
	FASTIO::read(n), FASTIO::read(m);
	inv[1] = 1;
	for (int i = 2; i <= n; ++i)
		inv[i] = -(mint::mod / i) * inv[mint::mod % i], assert(inv[i] * i == 1);
	$no_DS::solve();
	fwrite(FASTIO::obuf, 1, FASTIO::op - FASTIO::obuf, stdout);
    return 0;
}

/*
当 l > 1 时:
	相当于查的时候查 [l - 1, r - 1]
	sum[r, n] - sum[l - 1, n] = sum[l, r]
	-sum[l-1, r-1] = sum[l, r]
	sum[l, r] + sum[l-1, r-1] = 0
	a[l-1] + a[r] = 0
	a[l-1] = a[r]
	注意到,如果之前有一个东西横跨了 [l-1, r]
	// 设其概率 1/len = p
	// 设这次操作外 bl0=a[l-1][0], ...
	// 可以用操作后的 a 还原出来
	// 1. 恰落在了 l-1 或 r,概率 p * 2
	// 	ans += p * 2 * (bl1 * br0 + bl0 * br1)
	// 2. 落在了其他位置,概率 p * (len - 2)
	// 	ans += p * (len-2) * (bl0 * br0 + bl1 * br1)
	// 记 恰改变了一个位置 的概率为 p
	// 1. 恰改变了 l-1 或 r,概率 p
	// 	ans += p * (bl1 * br0 + bl0 * br1)
	// 2. 两者都没变,概率 1-p
	// 	ans += (1-p) * (bl0 * br0 + bl1 * br1)
	// a[i][0] = (1 - b0) * p + b0 * (1 - p)
	// a0 = p + b0 * (1 - p*2)
当 l = 1 时:
	sum[r, n] - 0 = sum[l, r]
	sum[r, n] = sum[l, r]
	sum[r+1, n] = sum[l, r-1]
	sum[r+1, n] = sum[1, r-1]
	记整个数组做了 x 次加法
	sum[1, n] = x
	sum[1, n] - sum[1, r-1] - a[r] = sum[1, r-1]
	sum[1, n] - a[r] = 2 * sum[1, r-1] = 0
	a[r] = x

静态区间?矩阵维护是也。夫预知答案如何,需维护若干矩阵。其困难之处在于,当 l>1 的情况难以搞。
正解肯定需要 CDQ

时间轴,CDQ 分治解决
左端点,归并解决
右端点,数据结构
*/

正常代码($673$ 行,略去取模板子)
#pragma GCC optimize("Ofast", "inline", "fast-math")

#include <cstdio>
#include <iostream>
#include <limits>
#include <cassert>
#include <cstring>
#include <algorithm>
using namespace std;

using namespace Mod_Int_Class;

char MST;

namespace FASTIO {

const int MAX = 1 << 26;
char buf[MAX], *ip = buf, obuf[MAX], *op = obuf;
#define putchar(x) *FASTIO::op++ = x
template <typename T>
inline void read(T &x) {
    x = 0; char ch = *ip++;
    for (; ch <  48; ch = *ip++);
    for (; ch >= 48; ch = *ip++) x = (x << 3) + (x << 1) + (ch ^ 48);
}
template <typename T>
inline void write(T x) {
    static short stack[20], top(0);
    do stack[++top] = x % 10; while (x /= 10);
    while (top) putchar(stack[top--] | 48);
}

}

namespace $yzh {

const int N = 100010;

int n, m, X;
mint inv[N];

mint ans[N];

namespace sub1 {

struct Matrix {
	mint a[4][4];
	
	inline void init() {
		memset(a, 0x00, sizeof(a));
	}
	
	inline void unit() {
		a[0][0] = 1;
		a[1][1] = 1;
		a[2][2] = 1;
		a[3][3] = 1;
	}
	
	mint * operator [] (int x) {
		return a[x];
	}
	
	mint const * operator [] (int x) const {
		return a[x];
	}
	
	friend Matrix operator * (const Matrix& a, const Matrix& b) {
		Matrix res;
		res[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0] + a[0][3] * b[3][0];
		res[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1] + a[0][3] * b[3][1];
		res[0][2] = a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2] + a[0][3] * b[3][2];
		res[0][3] = a[0][0] * b[0][3] + a[0][1] * b[1][3] + a[0][2] * b[2][3] + a[0][3] * b[3][3];
		res[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0] + a[1][3] * b[3][0];
		res[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1] + a[1][3] * b[3][1];
		res[1][2] = a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2] + a[1][3] * b[3][2];
		res[1][3] = a[1][0] * b[0][3] + a[1][1] * b[1][3] + a[1][2] * b[2][3] + a[1][3] * b[3][3];
		res[2][0] = a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0] + a[2][3] * b[3][0];
		res[2][1] = a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1] + a[2][3] * b[3][1];
		res[2][2] = a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2] + a[2][3] * b[3][2];
		res[2][3] = a[2][0] * b[0][3] + a[2][1] * b[1][3] + a[2][2] * b[2][3] + a[2][3] * b[3][3];
		res[3][0] = a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + a[3][3] * b[3][0];
		res[3][1] = a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + a[3][3] * b[3][1];
		res[3][2] = a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + a[3][3] * b[3][2];
		res[3][3] = a[3][0] * b[0][3] + a[3][1] * b[1][3] + a[3][2] * b[2][3] + a[3][3] * b[3][3];
		return res;
	}
	
	friend Matrix& operator *= (Matrix& a, const Matrix& b) {
		return a = a * b;
	}
};

struct Segment_Tree {
	Matrix a[N << 1];
	int ls[N << 1], rs[N << 1];
	bool tag[N << 1];
	int pcnt, rt;
	
	inline int newNode() {
		a[++pcnt].init();
		a[pcnt].unit();
		ls[pcnt] = rs[pcnt] = 0;
		tag[pcnt] = false;
		return pcnt;
	}
	
	void modify(int& idx, int trl, int trr, int p, const Matrix& x) {
		if (!idx) idx = newNode();
		if (tag[idx])
			a[idx] *= x;
		else
			a[idx] = x, tag[idx] = true;
		if (trl == trr) return;
		int mid = (trl + trr) >> 1;
		if (p <= mid)
			modify(ls[idx], trl, mid, p, x);
		else
			modify(rs[idx], mid + 1, trr, p, x);
	}
	
	inline void modify(int p, const Matrix& x) {
		modify(rt, 1, n, p, x);
	}
	
	inline void build() {
		pcnt = 0, rt = 0;
	}
	
	void query(int idx, int trl, int trr, int l, int r, Matrix& x) {
		if (l <= trl && trr <= r) {
			if (tag[idx])
				x *= a[idx];
			return;
		}
		int mid = (trl + trr) >> 1;
		if (ls[idx] && l <= mid)
			query(ls[idx], trl, mid, l, r, x);
		if (rs[idx] && r > mid)
			query(rs[idx], mid + 1, trr, l, r, x);
	}
	
	inline Matrix query(int l, int r) {
		Matrix t;
		t.init();
		t.unit();
		if (rt)
			query(rt, 1, n, l, r, t);
		return t;
	}
};

struct Bit_Tree {
	Matrix a[N];
	int T[N], timer;
	
	inline void refresh(int x) {
		if (T[x] != timer) {
			T[x] = timer;
			a[x].init();
			a[x].unit();
		}
	}
	
	inline void modify(int p, const Matrix& x) {
		for (; p; p &= p - 1)
			if (T[p] == timer) a[p] *= x;
			else T[p] = timer, a[p] = x;
	}
	
	inline void build() {
		++timer;
	}
	
	inline Matrix querysuf(int p) const {
		Matrix t;
		t.init();
		t.unit();
		for (; p <= n; p += p & -p)
			if (T[p] == timer)
				t *= a[p];
		return t;
	}
};

struct Question {
	int idx, l, r, T;
} q[N];
Matrix qv[N];

int qcnt;

void init() {
	
}

inline void add1(int l, int r) {
	q[++qcnt] = { -1, l, r, 0 };
    q[qcnt].T = qcnt;
}

inline void add2(int l, int r, int idx) {
	assert(l > 1);
	--l;
	q[++qcnt] = { idx, l, r, 0 };
    q[qcnt].T = qcnt;
	qv[idx].init();
	qv[idx].unit();
}

Bit_Tree t1;
Segment_Tree t2, t3;

void solve(int l, int r) {
	if (l == r) return;
	int mid = (l + r) >> 1;
	solve(l, mid), solve(mid + 1, r);
	
	t1.build(), t2.build();
	for (int i = mid + 1, j = l; i <= r; ++i) {
		for (; j <= mid && q[j].l <= q[i].l; ++j)
			if (!~q[j].idx) {
				int x = q[j].r;
				mint p = inv[q[j].r - q[j].l + 1];
				Matrix t;
				
				// 完全包含
				t.init();
				t[0b10][0b00] = p;
				t[0b01][0b00] = p;
				t[0b00][0b00] = 1 - p * 2;
				t[0b11][0b01] = p;
				t[0b00][0b01] = p;
				t[0b01][0b01] = 1 - p * 2;
				t[0b00][0b10] = p;
				t[0b11][0b10] = p;
				t[0b10][0b10] = 1 - p * 2;
				t[0b01][0b11] = p;
				t[0b10][0b11] = p;
				t[0b11][0b11] = 1 - p * 2;
				t1.modify(x, t);
				
				// 左端点
				t.init();
				t[0b00][0b00] = 1 - p;
				t[0b10][0b00] = p;
				t[0b10][0b10] = 1 - p;
				t[0b00][0b10] = p;
				t[0b01][0b01] = 1 - p;
				t[0b11][0b01] = p;
				t[0b11][0b11] = 1 - p;
				t[0b01][0b11] = p;
				t2.modify(x, t);
			}
		if (~q[i].idx) {
			qv[q[i].idx] *= t1.querysuf(q[i].r);
			if (q[i].l <= q[i].r - 1)
				qv[q[i].idx] *= t2.query(q[i].l, q[i].r - 1);
		}
	}
	
	inplace_merge(q + l, q + mid + 1, q + r + 1,
		[] (const Question& a, const Question& b) -> bool {
			return a.l < b.l;
		}
	);
}

void solve2(int l, int r) {
	if (l == r) return;
	int mid = (l + r) >> 1;
	solve2(l, mid), solve2(mid + 1, r);
    
    t3.build();
	for (int i = r, j = mid; i >= mid + 1; --i) {
		for (; j >= l && q[j].r >= q[i].r; --j)
			if (!~q[j].idx) {
				int x = q[j].l;
				mint p = inv[q[j].r - q[j].l + 1];
				Matrix t;
				
				t.init();
				t[0b00][0b00] = 1 - p;
				t[0b01][0b00] = p;
				t[0b01][0b01] = 1 - p;
				t[0b00][0b01] = p;
				t[0b10][0b10] = 1 - p;
				t[0b11][0b10] = p;
				t[0b11][0b11] = 1 - p;
				t[0b10][0b11] = p;
				t3.modify(x, t);
			}
		if (~q[i].idx) {
			if (q[i].l + 1 <= q[i].r)
				qv[q[i].idx] *= t3.query(q[i].l + 1, q[i].r);
		}
	}
    
    inplace_merge(q + l, q + mid + 1, q + r + 1,
		[] (const Question& a, const Question& b) -> bool {
			return a.r < b.r;
		}
	);
}

void solve() {
	solve(1, qcnt);
    sort(q + 1, q + qcnt + 1, [] (const Question& a, const Question& b) -> bool {
        return a.T < b.T;
    });
    solve2(1, qcnt);
	
	for (int l = 1; l <= qcnt; ++l)
		if (~q[l].idx) {
			Matrix t;
			t.init();
			t[0][0] = 1;
			t *= qv[q[l].idx];
			ans[q[l].idx] = t[0][0] + t[0][3];
		}
}

}

namespace sub2 {

struct Matrix {
	mint a[2][2];
	
	inline void init() {
		memset(a, 0x00, sizeof(a));
	}
	
	inline void unit() {
		a[0][0] = 1;
		a[1][1] = 1;
	}
	
	mint * operator [] (int x) {
		return a[x];
	}
	
	mint const * operator [] (int x) const {
		return a[x];
	}
	
	friend Matrix operator * (const Matrix& a, const Matrix& b) {
		Matrix res;
		res[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0];
		res[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1];
		res[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0];
		res[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1];
		return res;
	}
	
	friend Matrix& operator *= (Matrix& a, const Matrix& b) {
		return a = a * b;
	}
};

struct Segment_Tree {
	Matrix a[N << 1];
	int ls[N << 1], rs[N << 1];
	bool tag[N << 1];
	int pcnt, rt;
	
	inline int newNode() {
		a[++pcnt].init();
		a[pcnt].unit();
		ls[pcnt] = rs[pcnt] = 0;
		tag[pcnt] = false;
		return pcnt;
	}
	
	void modify(int& idx, int trl, int trr, int l, int r, const Matrix& x) {
		if (!idx) idx = newNode();
		if (l <= trl && trr <= r) {
			if (tag[idx])
				a[idx] *= x;
			else
				a[idx] = x, tag[idx] = true;
			return;
		}
		int mid = (trl + trr) >> 1;
		if (l <= mid) modify(ls[idx], trl, mid, l, r, x);
		if (r >  mid) modify(rs[idx], mid + 1, trr, l, r, x);
	}
	
	inline void modify(int l, int r, const Matrix& x) {
		modify(rt, 1, n, l, r, x);
	}
	
	inline void build() {
		pcnt = 0, rt = 0;
	}
	
	void query(int idx, int trl, int trr, int p, Matrix& x) {
		if (tag[idx])
			x *= a[idx];
		if (trl == trr) return;
		int mid = (trl + trr) >> 1;
		if (ls[idx] && p <= mid)
			query(ls[idx], trl, mid, p, x);
		else if (rs[idx] && p > mid)
			query(rs[idx], mid + 1, trr, p, x);
	}
	
	inline Matrix query(int p) {
		Matrix t;
		t.init();
		t.unit();
		if (rt)
			query(rt, 1, n, p, t);
		return t;
	}
};
	
Segment_Tree yzh;
int X;

inline void init() {
	yzh.build(), X = 0;
}

void add1(int l, int r) {
	Matrix t;
	t.init();
	mint p = inv[r - l + 1];
	t[0][0] = 1 - p, t[0][1] = p;
	t[1][0] = p, t[1][1] = 1 - p;
	yzh.modify(l, r, t);
	++X;
}

void add2(int x, int idx) {
	Matrix res = yzh.query(x);
	Matrix t;
	t.init();
	t[0][0] = 1, t[0][1] = 0;
	t = t * res;
	ans[idx] = t[0][X & 1];
}

void solve() {
	
}

}

bool isQ[N];

void solve() {
	FASTIO::read(n), FASTIO::read(m);
	inv[1] = 1;
	for (int i = 2; i <= n; ++i)
		inv[i] = -(mint::mod / i) * inv[mint::mod % i], assert(inv[i] * i == 1);
	sub1::init(), sub2::init();
	for (int op, l, r, i = 1; i <= m; ++i) {
		FASTIO::read(op);
		FASTIO::read(l), FASTIO::read(r);
		if (op == 1) {
			sub1::add1(l, r);
			sub2::add1(l, r);
		} else {
			isQ[i] = true;
			if (l > 1) {
				sub1::add2(l, r, i);
			} else {
				sub2::add2(r, i);
			}
		}
	}
	sub1::solve(), sub2::solve();
	for (int i = 1; i <= m; ++i)
		if (isQ[i])
			FASTIO::write(ans[i].raw()), putchar('\n');
}

}

char MED;

int main() {
	fprintf(stderr, "Memory: %.2lfMB\n", (&MED - &MST) / 1024. / 1024);
#ifndef XuYueming
	// freopen("bit.in", "r", stdin);
	// freopen("bit.out", "w", stdout);
#endif
	fread(FASTIO::buf, 1, FASTIO::MAX, stdin);
	$yzh::solve();
	fwrite(FASTIO::obuf, 1, FASTIO::op - FASTIO::obuf, stdout);
    return 0;
}
谁说我代码太长了?
#include<bits/stdc++.h>
using namespace std;namespace Mod_Int_Class{template<typename T,typename _Tp>constexpr bool in_range(_Tp val){return std::numeric_limits<T>::min()<=val&&val<=std::numeric_limits<T>::max();}template<typename _Tp,typename=std::enable_if_t<std::is_integral<_Tp>::value>>static constexpr inline bool is_prime(_Tp val){if(val<2)return false;for(_Tp i=2;i*i<=val;++i)if(val%i==0)return false;return true;}template<auto _mod=998244353,typename T=int,typename S=long long>class Mod_Int{static_assert(in_range<T>(_mod),"mod must in the range of type T.");static_assert(std::is_integral<T>::value,"type T must be an integer.");static_assert(std::is_integral<S>::value,"type S must be an integer.");public:constexpr Mod_Int()noexcept=default;template<typename _Tp,typename=std::enable_if_t<std::is_integral<_Tp>::value>>constexpr Mod_Int(_Tp v)noexcept:val(0){if(0<=S(v)&&S(v)<mod)val=v;else val=(S(v)%mod+mod)%mod;}constexpr T const&raw()const{return this->val;}static constexpr T mod=_mod;template<typename _Tp,typename=std::enable_if_t<std::is_integral<_Tp>::value>>constexpr friend Mod_Int pow(Mod_Int a,_Tp p){return a^p;}constexpr friend Mod_Int sub(Mod_Int a,Mod_Int b){return a-b;}constexpr friend Mod_Int&tosub(Mod_Int&a,Mod_Int b){return a-=b;}constexpr friend Mod_Int add(Mod_Int a){return a;}template<typename...args_t>constexpr friend Mod_Int add(Mod_Int a,args_t...args){return a+add(args...);}constexpr friend Mod_Int mul(Mod_Int a){return a;}template<typename...args_t>constexpr friend Mod_Int mul(Mod_Int a,args_t...args){return a*mul(args...);}template<typename...args_t>constexpr friend Mod_Int&toadd(Mod_Int&a,args_t...b){return a=add(a,b...);}template<typename...args_t>constexpr friend Mod_Int&tomul(Mod_Int&a,args_t...b){return a=mul(a,b...);}template<T __mod=mod,typename=std::enable_if_t<is_prime(__mod)>>static constexpr inline T inv(T a){assert(a!=0);return _pow(a,mod-2);}constexpr Mod_Int&operator+()const{return*this;}constexpr Mod_Int operator-()const{return _sub(0,val);}constexpr Mod_Int inv()const{return inv(val);}constexpr friend inline Mod_Int operator+(Mod_Int a,Mod_Int b){return _add(a.val,b.val);}constexpr friend inline Mod_Int operator-(Mod_Int a,Mod_Int b){return _sub(a.val,b.val);}constexpr friend inline Mod_Int operator*(Mod_Int a,Mod_Int b){return _mul(a.val,b.val);}constexpr friend inline Mod_Int operator/(Mod_Int a,Mod_Int b){return _mul(a.val,inv(b.val));}template<typename _Tp,typename=std::enable_if_t<std::is_integral<_Tp>::value>>constexpr friend inline Mod_Int operator^(Mod_Int a,_Tp p){return _pow(a.val,p);}constexpr friend inline Mod_Int&operator+=(Mod_Int&a,Mod_Int b){return a=_add(a.val,b.val);}constexpr friend inline Mod_Int&operator-=(Mod_Int&a,Mod_Int b){return a=_sub(a.val,b.val);}constexpr friend inline Mod_Int&operator*=(Mod_Int&a,Mod_Int b){return a=_mul(a.val,b.val);}constexpr friend inline Mod_Int&operator/=(Mod_Int&a,Mod_Int b){return a=_mul(a.val,inv(b.val));}template<typename _Tp,typename=std::enable_if_t<std::is_integral<_Tp>::value>>constexpr friend inline Mod_Int&operator^=(Mod_Int&a,_Tp p){return a=_pow(a.val,p);}constexpr friend inline bool operator==(Mod_Int a,Mod_Int b){return a.val==b.val;}constexpr friend inline bool operator!=(Mod_Int a,Mod_Int b){return a.val!=b.val;}constexpr Mod_Int&operator++(){this->val+1==mod?this->val=0:++this->val;return*this;}constexpr Mod_Int&operator--(){this->val==0?this->val=mod-1:--this->val;return*this;}constexpr Mod_Int operator++(int){Mod_Int res=*this;this->val+1==mod?this->val=0:++this->val;return res;}constexpr Mod_Int operator--(int){Mod_Int res=*this;this->val==0?this->val=mod-1:--this->val;return res;}friend std::istream&operator>>(std::istream&is,Mod_Int<mod,T,S>&x){T ipt;return is>>ipt,x=ipt,is;}friend std::ostream&operator<<(std::ostream&os,Mod_Int<mod,T,S>x){return os<<x.val;}protected:T val;static constexpr inline T _add(T a,T b){return a>=mod-b?a+b-mod:a+b;}static constexpr inline T _sub(T a,T b){return a<b?a-b+mod:a-b;}static constexpr inline T _mul(T a,T b){return static_cast<S>(a)*b%mod;}template<typename _Tp,typename=std::enable_if_t<std::is_integral<_Tp>::value>>static constexpr inline T _pow(T a,_Tp p){T res=1;for(;p;p>>=1,a=_mul(a,a))if(p&1)res=_mul(res,a);return res;}};using mint=Mod_Int<>;using mod_t=mint;constexpr mint operator""_m(unsigned long long x){return mint(x);}constexpr mint operator""_mod(unsigned long long x){return mint(x);}}using namespace Mod_Int_Class;namespace FASTIO{const int MAX=1<<26;char buf[MAX],*ip=buf,obuf[MAX],*op=obuf;template<typename T>inline void read(T&x){x=0;char ch=*ip++;for(;ch<48;ch=*ip++);for(;ch>=48;ch=*ip++)x=(x<<3)+(x<<1)+(ch^48);}template<typename T>inline void write(T x){static short stack[20],top(0);do stack[++top]=x%10;while(x/=10);while(top)*FASTIO::op++=(stack[top--]|48);}}namespace $yzh{const int N=100010;int n,m,X;mint inv[N],ans[N];namespace sub1{struct Matrix{mint a[4][4];inline void init(){memset(a,0x00,sizeof(a));}inline void unit(){a[0][0]=1;a[1][1]=1;a[2][2]=1;a[3][3]=1;}mint*operator[](int x){return a[x];}mint const*operator[](int x)const{return a[x];}friend Matrix operator*(const Matrix&a,const Matrix&b){Matrix res;res[0][0]=a[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0]+a[0][3]*b[3][0];res[0][1]=a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1]+a[0][3]*b[3][1];res[0][2]=a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2]+a[0][3]*b[3][2];res[0][3]=a[0][0]*b[0][3]+a[0][1]*b[1][3]+a[0][2]*b[2][3]+a[0][3]*b[3][3];res[1][0]=a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0]+a[1][3]*b[3][0];res[1][1]=a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1]+a[1][3]*b[3][1];res[1][2]=a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2]+a[1][3]*b[3][2];res[1][3]=a[1][0]*b[0][3]+a[1][1]*b[1][3]+a[1][2]*b[2][3]+a[1][3]*b[3][3];res[2][0]=a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0]+a[2][3]*b[3][0];res[2][1]=a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1]+a[2][3]*b[3][1];res[2][2]=a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2]+a[2][3]*b[3][2];res[2][3]=a[2][0]*b[0][3]+a[2][1]*b[1][3]+a[2][2]*b[2][3]+a[2][3]*b[3][3];res[3][0]=a[3][0]*b[0][0]+a[3][1]*b[1][0]+a[3][2]*b[2][0]+a[3][3]*b[3][0];res[3][1]=a[3][0]*b[0][1]+a[3][1]*b[1][1]+a[3][2]*b[2][1]+a[3][3]*b[3][1];res[3][2]=a[3][0]*b[0][2]+a[3][1]*b[1][2]+a[3][2]*b[2][2]+a[3][3]*b[3][2];res[3][3]=a[3][0]*b[0][3]+a[3][1]*b[1][3]+a[3][2]*b[2][3]+a[3][3]*b[3][3];return res;}friend Matrix&operator*=(Matrix&a,const Matrix&b){return a=a*b;}};struct Segment_Tree{Matrix a[N<<1];int ls[N<<1],rs[N<<1];bool tag[N<<1];int pcnt,rt;inline int newNode(){a[++pcnt].init();a[pcnt].unit();ls[pcnt]=rs[pcnt]=0;tag[pcnt]=false;return pcnt;}void modify(int&idx,int trl,int trr,int p,const Matrix&x){if(!idx)idx=newNode();if(tag[idx])a[idx]*=x;else a[idx]=x,tag[idx]=true;if(trl==trr)return;int mid=(trl+trr)>>1;if(p<=mid)modify(ls[idx],trl,mid,p,x);else modify(rs[idx],mid+1,trr,p,x);}inline void modify(int p,const Matrix&x){modify(rt,1,n,p,x);}inline void build(){pcnt=0,rt=0;}void query(int idx,int trl,int trr,int l,int r,Matrix&x){if(l<=trl&&trr<=r){if(tag[idx])x*=a[idx];return;}int mid=(trl+trr)>>1;if(ls[idx]&&l<=mid)query(ls[idx],trl,mid,l,r,x);if(rs[idx]&&r>mid)query(rs[idx],mid+1,trr,l,r,x);}inline Matrix query(int l,int r){Matrix t;t.init();t.unit();if(rt)query(rt,1,n,l,r,t);return t;}};struct Bit_Tree{Matrix a[N];int T[N],timer;inline void refresh(int x){if(T[x]!=timer){T[x]=timer;a[x].init();a[x].unit();}}inline void modify(int p,const Matrix&x){for(;p;p&=p-1)if(T[p]==timer)a[p]*=x;else T[p]=timer,a[p]=x;}inline void build(){++timer;}inline Matrix querysuf(int p)const{Matrix t;t.init();t.unit();for(;p<=n;p+=p&-p)if(T[p]==timer)t*=a[p];return t;}};struct Question{int idx,l,r,T;}q[N];Matrix qv[N];int qcnt;void init(){}inline void add1(int l,int r){q[++qcnt]={-1,l,r,0};q[qcnt].T=qcnt;}inline void add2(int l,int r,int idx){assert(l>1);--l;q[++qcnt]={idx,l,r,0};q[qcnt].T=qcnt;qv[idx].init();qv[idx].unit();}Bit_Tree t1;Segment_Tree t2,t3;void solve(int l,int r){if(l==r)return;int mid=(l+r)>>1;solve(l,mid),solve(mid+1,r);t1.build(),t2.build();for(int i=mid+1,j=l;i<=r;++i){for(;j<=mid&&q[j].l<=q[i].l;++j)if(!~q[j].idx){int x=q[j].r;mint p=inv[q[j].r-q[j].l+1];Matrix t;t.init();t[0b10][0b00]=p;t[0b01][0b00]=p;t[0b00][0b00]=1-p*2;t[0b11][0b01]=p;t[0b00][0b01]=p;t[0b01][0b01]=1-p*2;t[0b00][0b10]=p;t[0b11][0b10]=p;t[0b10][0b10]=1-p*2;t[0b01][0b11]=p;t[0b10][0b11]=p;t[0b11][0b11]=1-p*2;t1.modify(x,t);t.init();t[0b00][0b00]=1-p;t[0b10][0b00]=p;t[0b10][0b10]=1-p;t[0b00][0b10]=p;t[0b01][0b01]=1-p;t[0b11][0b01]=p;t[0b11][0b11]=1-p;t[0b01][0b11]=p;t2.modify(x,t);}if(~q[i].idx){qv[q[i].idx]*=t1.querysuf(q[i].r);if(q[i].l<=q[i].r-1)qv[q[i].idx]*=t2.query(q[i].l,q[i].r-1);}}inplace_merge(q+l,q+mid+1,q+r+1,[](const Question&a,const Question&b)->bool{return a.l<b.l;});}void solve2(int l,int r){if(l==r)return;int mid=(l+r)>>1;solve2(l,mid),solve2(mid+1,r);t3.build();for(int i=r,j=mid;i>=mid+1;--i){for(;j>=l&&q[j].r>=q[i].r;--j)if(!~q[j].idx){int x=q[j].l;mint p=inv[q[j].r-q[j].l+1];Matrix t;t.init();t[0b00][0b00]=1-p;t[0b01][0b00]=p;t[0b01][0b01]=1-p;t[0b00][0b01]=p;t[0b10][0b10]=1-p;t[0b11][0b10]=p;t[0b11][0b11]=1-p;t[0b10][0b11]=p;t3.modify(x,t);}if(~q[i].idx){if(q[i].l+1<=q[i].r)qv[q[i].idx]*=t3.query(q[i].l+1,q[i].r);}}inplace_merge(q+l,q+mid+1,q+r+1,[](const Question&a,const Question&b)->bool{return a.r<b.r;});}void solve(){solve(1,qcnt);sort(q+1,q+qcnt+1,[](const Question&a,const Question&b)->bool{return a.T<b.T;});solve2(1,qcnt);for(int l=1;l<=qcnt;++l)if(~q[l].idx){Matrix t;t.init();t[0][0]=1;t*=qv[q[l].idx];ans[q[l].idx]=t[0][0]+t[0][3];}}}namespace sub2{struct Matrix{mint a[2][2];inline void init(){memset(a,0x00,sizeof(a));}inline void unit(){a[0][0]=1;a[1][1]=1;}mint*operator[](int x){return a[x];}mint const*operator[](int x)const{return a[x];}friend Matrix operator*(const Matrix&a,const Matrix&b){Matrix res;res[0][0]=a[0][0]*b[0][0]+a[0][1]*b[1][0];res[0][1]=a[0][0]*b[0][1]+a[0][1]*b[1][1];res[1][0]=a[1][0]*b[0][0]+a[1][1]*b[1][0];res[1][1]=a[1][0]*b[0][1]+a[1][1]*b[1][1];return res;}friend Matrix&operator*=(Matrix&a,const Matrix&b){return a=a*b;}};struct Segment_Tree{Matrix a[N<<1];int ls[N<<1],rs[N<<1];bool tag[N<<1];int pcnt,rt;inline int newNode(){a[++pcnt].init();a[pcnt].unit();ls[pcnt]=rs[pcnt]=0;tag[pcnt]=false;return pcnt;}void modify(int&idx,int trl,int trr,int l,int r,const Matrix&x){if(!idx)idx=newNode();if(l<=trl&&trr<=r){if(tag[idx])a[idx]*=x;else a[idx]=x,tag[idx]=true;return;}int mid=(trl+trr)>>1;if(l<=mid)modify(ls[idx],trl,mid,l,r,x);if(r>mid)modify(rs[idx],mid+1,trr,l,r,x);}inline void modify(int l,int r,const Matrix&x){modify(rt,1,n,l,r,x);}inline void build(){pcnt=0,rt=0;}void query(int idx,int trl,int trr,int p,Matrix&x){if(tag[idx])x*=a[idx];if(trl==trr)return;int mid=(trl+trr)>>1;if(ls[idx]&&p<=mid)query(ls[idx],trl,mid,p,x);else if(rs[idx]&&p>mid)query(rs[idx],mid+1,trr,p,x);}inline Matrix query(int p){Matrix t;t.init();t.unit();if(rt)query(rt,1,n,p,t);return t;}};Segment_Tree yzh;int X;inline void init(){yzh.build(),X=0;}void add1(int l,int r){Matrix t;t.init();mint p=inv[r-l+1];t[0][0]=1-p,t[0][1]=p;t[1][0]=p,t[1][1]=1-p;yzh.modify(l,r,t);++X;}void add2(int x,int idx){Matrix res=yzh.query(x);Matrix t;t.init();t[0][0]=1,t[0][1]=0;t=t*res;ans[idx]=t[0][X&1];}void solve(){}}bool isQ[N];void solve(){FASTIO::read(n),FASTIO::read(m);inv[1]=1;for(int i=2;i<=n;++i)inv[i]=-(mint::mod/i)*inv[mint::mod%i],assert(inv[i]*i==1);sub1::init(),sub2::init();for(int op,l,r,i=1;i<=m;++i){FASTIO::read(op);FASTIO::read(l),FASTIO::read(r);if(op==1){sub1::add1(l,r);sub2::add1(l,r);}else{isQ[i]=true;if(l>1){sub1::add2(l,r,i);}else{sub2::add2(r,i);}}}sub1::solve(),sub2::solve();for(int i=1;i<=m;++i)if(isQ[i])FASTIO::write(ans[i].raw()),*FASTIO::op++ = ('\n');}}int main(){freopen("bit.in","r",stdin);freopen("bit.out","w",stdout);fread(FASTIO::buf,1,FASTIO::MAX,stdin);$yzh::solve();fwrite(FASTIO::obuf,1,FASTIO::op-FASTIO::obuf,stdout);return 0;}
posted @ 2025-02-03 15:09  XuYueming  阅读(51)  评论(0)    收藏  举报