要是早点写这题就能赛时调过了,就能Rank2了(痛哭
看到题目先来化一波式子:
为了简化式子,令,
,
发现长得都很像,所以其实只要求出形如的东西就行了。
考虑每个位置的权值对这个式子上面的指数的贡献,我们可以得知这个式子就等于 。
组合意义是每个位置选或者不选的所有情况,最后再减去空集。
发现这个就很好拿线段树维护了。
具体的,线段树上每个节点维护区间所有前缀积的和,区间所有后缀积的和
,区间所有数的乘积
,区间以及所有子区间的答案
。
那么的时候,有
然后就是一开始线段树上每个位置的初值如何处理,开个结构体代表某个数是的形式,重载运算符
和
就行。
根据数的运算规则,最后
的系数一定为
。
其实不需要算三遍,由于和
是共轭的所以对答案贡献一样。
由于和
的值域极小只有
,所以预处理一下用数组存起来就行了。
/*
_______ ________ _______
/ _____ \ / ______ \ / _____ \
/ / \_\ _ __ _ / / \ \ _ __ _ / / \_\
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | __ | | | | | | | | | |
| | __ \ \ | | / / | | \ \| | \ \ | | / / | | __
\ \_____/ / \ \/ /\ \/ / \ \_____\ / \ \/ /\ \/ / \ \_____/ /
\_______/ \___/ \___/ \______/\__\ \___/ \___/ \_______/
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100000;
const int MOD = 998244353;
const int div2 = 499122177;
const int div5 = 598946612;
int a[N + 50], n, q;
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? - x : x;
return;
}
struct Tmp
{
int a, b;
Tmp operator * (const Tmp &rhs) const
{
return (Tmp){(int)(1LL * b * rhs.a % MOD + 1LL * a * rhs.b % MOD) % MOD, (int)((1LL * a * rhs.a % MOD) * 5 % MOD + 1LL * b * rhs.b % MOD) % MOD};
}
Tmp operator + (const Tmp &rhs) const
{
return (Tmp){(a + rhs.a) % MOD, (b + rhs.b) % MOD};
}
} prework[N + 50][3];
struct Tmpp
{
Tmp s0, s1, pre0, pre1, suf0, suf1, ans0, ans1;
};
void Prework()
{
Tmp work0 = (Tmp){ div2, 1LL * 3 * div2 % MOD}, work1 = (Tmp){0, -1};
prework[1][0] = work0;
prework[1][1] = work1;
for (int i = 2; i <= N; i++)
prework[i][0] = prework[i - 1][0] * work0,
prework[i][1] = prework[i - 1][1] * work1;
return;
}
struct SegmentTree
{
Tmp s[(N << 2) + 50][2], pre[(N << 2) + 50][2], suf[(N << 2) + 50][2], ans[(N << 2) + 50][2];
void Pushup(int k, int l, int r)
{
for (int i = 0; i <= 1; i++)
s[k][i] = s[l][i] * s[r][i],
pre[k][i] = s[l][i] * pre[r][i] + pre[l][i],
suf[k][i] = suf[l][i] * s[r][i] + suf[r][i],
ans[k][i] = ans[l][i] + ans[r][i] + suf[l][i] * pre[r][i];
return;
}
void Build(int k, int l, int r)
{
if (l == r)
{
for (int i = 0; i <= 1; i++) s[k][i] = pre[k][i] = suf[k][i] = ans[k][i] = (Tmp){prework[a[l]][i].a, (prework[a[l]][i].b + 1) % MOD};
return;
}
int mid = (l + r) >> 1;
Build(k << 1, l, mid); Build(k << 1 | 1, mid + 1, r);
Pushup(k, k << 1, k << 1 | 1);
return;
}
void Update(int k, int l, int r, int pos, int zhi)
{
if (l == r)
{
for (int i = 0; i <= 1; i++) s[k][i] = pre[k][i] = suf[k][i] = ans[k][i] = (Tmp){prework[zhi][i].a, (prework[zhi][i].b + 1) % MOD};
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) Update(k << 1, l, mid, pos, zhi);
else Update(k << 1 | 1, mid + 1, r, pos, zhi);
Pushup(k, k << 1, k << 1 | 1);
return;
}
Tmpp Query(int k, int l, int r, int x, int y)
{
if (x <= l && r <= y) return (Tmpp){s[k][0], s[k][1], pre[k][0], pre[k][1], suf[k][0], suf[k][1], ans[k][0], ans[k][1]};
int mid = (l + r) >> 1;
if (y <= mid) return Query(k << 1, l, mid, x, y);
else if (x > mid) return Query(k << 1 | 1, mid + 1, r, x, y);
else
{
Tmpp ql = Query(k << 1, l, mid, x, y), qr = Query(k << 1 | 1, mid + 1, r, x, y), tmpp;
tmpp.s0 = ql.s0 * qr.s0,
tmpp.pre0 = ql.s0 * qr.pre0 + ql.pre0,
tmpp.suf0 = ql.suf0 * qr.s0 + qr.suf0;
tmpp.ans0 = ql.ans0 + qr.ans0 + ql.suf0 * qr.pre0;
tmpp.s1 = ql.s1 * qr.s1,
tmpp.pre1 = ql.s1 * qr.pre1 + ql.pre1,
tmpp.suf1 = ql.suf1 * qr.s1 + qr.suf1;
tmpp.ans1 = ql.ans1 + qr.ans1 + ql.suf1 * qr.pre1;
return tmpp;
}
}
} tr;
int main()
{
Prework();
Read(n); Read(q);
for (int i = 1; i <= n; i++) Read(a[i]);
tr.Build(1, 1, n);
for (int i = 1, opt, l, r; i <= q; i++)
{
Read(opt); Read(l); Read(r);
if (opt == 1) tr.Update(1, 1, n, l, r);
else
{
Tmpp tmp = tr.Query(1, 1, n, l, r);
int ans = 0;
ans = (ans + 1LL * tmp.ans0.b * 2 % MOD - 2) % MOD;
ans = (ans - 1LL * 2 * (tmp.ans1.b - 1) % MOD + MOD) % MOD;
ans = 1LL * ans * div5 % MOD;
printf("%d\n", ans);
}
}
return 0;
} 
京公网安备 11010502036488号