前言
题目链接:洛谷;UOJ;LOJ。
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\) 与否进行讨论。
- \(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\) 的概率。 - \(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\) 的矩阵,那么对于一个修改,就可以构造出转移矩阵:
然后,我们请出一个能够区间乘矩阵,单点查询的数据结构。线段树是也。
于是,我们在线地解决了问题一,修改 / 询问 \(\mathcal{O}(w^3\log n)\),其中 \(w=2\)。
问题二
类似的,我们把 \(f[0/1][0/1]\) 拍扁成一个 \(1\times 4\) 的矩阵,并构造出转移矩阵。
- 完全包含
- 仅包含左端点
- 仅包含右端点
但是,这时候,我们发现有了区间包含关系的限制,十分地不好搞。
发现如果拍到一个二维笛卡尔坐标系上,这是二维数点状物。俗话说得好,二维数点,静态扫描线而动态 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 falsenamespace $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());}}
}}#endifnamespace $no_DS {#if falsetemplate <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");}}
};#endifmint 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;elsea[idx] = x, tag[idx] = true;if (trl == trr) return;int mid = (trl + trr) >> 1;if (p <= mid)modify(ls[idx], trl, mid, p, x);elsemodify(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;elsea[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);
#endiffread(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] = 0a[l-1] + a[r] = 0a[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] = xsum[1, n] - sum[1, r-1] - a[r] = sum[1, r-1]sum[1, n] - a[r] = 2 * sum[1, r-1] = 0a[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;elsea[idx] = x, tag[idx] = true;if (trl == trr) return;int mid = (trl + trr) >> 1;if (p <= mid)modify(ls[idx], trl, mid, p, x);elsemodify(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;elsea[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);
#endiffread(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;}