可以发现,这个树状数组实际上求的是后缀和。而这样子求出的区间和,只有
l−1
和
r
两个点的差别。于是第二问就转化成
这里设
f(i,j)
表示
A[i]=A[j]
的概率,
f(0,j)
表示
∑ji=1A[i]≡∑ni=jA[i](mod2)
的概率。
Q:为什么要维护任意两个点相等的概率,而不是维护一个点为
1
的概率
A:这里每个点为
1
的概率不是独立的,所以结果不一定是
回到问题,考虑一个修改操作对
f
的影响。分
1、
i,j
两个端点,一个在
[l,r]
内,一个在
[l,r]
外。那么这时候的
A[i]=A[j]
的真假就有
1r−l+1
的概率被反转。即
∀i∈[1,l−1],j∈[l,r]⋁i∈[l,r],j∈[r+1,n]
,
f(i,j)=f(i,j)∗r−lr−l+1+(1−f(i,j))∗1r−l+1
。
2、
i,j
两个端点都在
[l,r]
内。则此时
A[i]=A[j]
的真假被反转的条件是
i
被反转或
3、
i=0
,
j
在
4、
i=0
,
j
在
而询问就是询问
f(l−1,r)
的值。
可以用线段树套线段树维护
f
<script type="math/tex" id="MathJax-Element-134">f</script>的值,支持二维区间修改和单点询问。实现见代码。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 4e5 + 193, W = 4e7 + 97, PYZ = 998244353;
int n, m, rt[N], QAQ;
struct cyx {
int lc, rc, val;
void init() {lc = rc = 0; val = 1;}
} T[W];
int qpow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = 1ll * res * a % PYZ;
a = 1ll * a * a % PYZ;
b >>= 1;
}
return res;
}
int calc(int x, int y) {
int res = 1ll * x * y % PYZ, u = 1 - x, v = 1 - y;
if (u < 0) u += PYZ; if (v < 0) v += PYZ;
return (res + 1ll * u * v % PYZ) % PYZ;
}
void modify(int l, int r, int s, int e, int v, int &p) {
if (!p) T[p = ++QAQ].init();
if (l == s && r == e) return (void) (T[p].val = calc(T[p].val, v));
int mid = l + r >> 1;
if (e <= mid) modify(l, mid, s, e, v, T[p].lc);
else if (s >= mid + 1) modify(mid + 1, r, s, e, v, T[p].rc);
else modify(l, mid, s, mid, v, T[p].lc),
modify(mid + 1, r, mid + 1, e, v, T[p].rc);
}
void change(int l, int r, int s, int e, int st, int ed, int v, int p) {
if (l == s && r == e) return modify(1, n, st, ed, v, rt[p]);
int mid = l + r >> 1;
if (e <= mid) change(l, mid, s, e, st, ed, v, p2);
else if (s >= mid + 1) change(mid + 1, r, s, e, st, ed, v, p3);
else change(l, mid, s, mid, st, ed, v, p2),
change(mid + 1, r, mid + 1, e, st, ed, v, p3);
}
int query(int l, int r, int x, int p) {
if (!p) return 1; int res = T[p].val;
if (l == r) return res; int mid = l + r >> 1;
if (x <= mid) res = calc(res, query(l, mid, x, T[p].lc));
else res = calc(res, query(mid + 1, r, x, T[p].rc));
return res;
}
int ask(int l, int r, int x, int y, int p) {
int res = query(1, n, y, rt[p]);
if (l == r) return res; int mid = l + r >> 1;
if (x <= mid) res = calc(res, ask(l, mid, x, y, p2));
else res = calc(res, ask(mid + 1, r, x, y, p3));
return res;
}
int main() {
int i, op, x, y; n = read(); m = read();
while (m--) {
op = read(); x = read(); y = read();
if (op == 1) {
int v = qpow(y - x + 1, PYZ - 2), w, u = (1 - v + PYZ) % PYZ;
if (x > 1) change(0, n, 1, x - 1, x, y, u, 1);
if (y < n) change(0, n, x, y, y + 1, n, u, 1);
w = (1 - (v << 1) % PYZ + PYZ) % PYZ;
change(0, n, x, y, x, y, w, 1);
if (x > 1) change(0, n, 0, 0, 1, x - 1, 0, 1);
if (y < n) change(0, n, 0, 0, y + 1, n, 0, 1);
change(0, n, 0, 0, x, y, v, 1);
}
else printf("%d\n", ask(0, n, x - 1, y, 1));
}
return 0;
}