有一个递推数列 ,
对于一个长度为 的区间,共有 次操作,每个操作给三个数字 ,你需要支持下面两种操作
:区间 每个位置加上
:输出区间 的和膜 的值
首先肯定要找到递推数列 的性质,显然直接在线段树上打标记是不行的,那么我们可以考虑先求出递推数列 的通项
首先假设 递推数列 的递推式为 , 并且存在 满足 ,将这两个式子联立得到 ,可以求出
若
,继续化简得到
将 代入上述式子得到
同理可以得到
将 代入上述式子得到
化简得到
若
将 代入 得到两个式子,并且递推数列 是等比数列
,继续化简得到
,继续化简得到
首先用二次剩余和逆元将式子中出现的根号和分数化为整数。
所以题目中的两个操作就变成了区间加等比数列,区间求和,然后使用两个线段树分别维护两个次方即可
// F_n = \frac{1}{\sqrt{17}}*((\frac{3+\sqrt{17}}{2})^n-(\frac{3-\sqrt{17}}{2})^n)
// F_n = inv_sqrt17*(x_0^n-x_1^n)
#include
#define ll long long
#define sc scanf
#define pr printf
#define lson left,mid,k<<1,op
#define rson mid+1,right,k<<1|1,op
#define imid int mid=(left+right)>>1;
using namespace std;
const int MAXN = 1e5 + 5;
const ll mod = 998244353;
const ll sqrt17 = 473844410;
const ll inv_sqrt17 = 438914993;
const ll x[2] = { 736044383,262199973 };
struct node
{
int l;
int r;
ll mark;
ll sum;
}que[MAXN * 4][2];
ll a[MAXN];
ll facq[MAXN][2]; //power(x[op],i)
const ll inv[2] = { 932694360,315111081 }; //power(x[op]-1,mod-2)
void init()
{
facq[0][0] = 1;
facq[0][1] = 1;
for (int i = 1; i < MAXN; i++)
{
facq[i][0] = facq[i - 1][0] * x[0] % mod;
facq[i][1] = facq[i - 1][1] * x[1] % mod;
}
}
ll power(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll calcnth(ll a1, ll n, int op)
{
ll an = a1 * facq[n - 1][op] % mod;
return an;
}
ll calcsum(ll a1, ll n, ll op)
{
ll q = x[op];
ll an = a1 * facq[n - 1][op] % mod;
ll ans = (an * q % mod - a1 + mod) * inv[op] % mod;
return ans;
}
void up(int k, int op)
{
que[k][op].sum = (que[k << 1][op].sum + que[k << 1 | 1][op].sum) % mod;
}
void down(int k, int op)
{
if (que[k][op].mark)
{
que[k << 1][op].mark = (que[k << 1][op].mark + que[k][op].mark) % mod;
que[k << 1][op].sum = (que[k << 1][op].sum + calcsum(que[k][op].mark, (que[k << 1][op].r - que[k << 1][op].l + 1), op)) % mod;
ll rst = calcnth(que[k][op].mark, que[k << 1][op].r - que[k << 1][op].l + 1 + 1, op);
que[k << 1 | 1][op].mark = (que[k << 1 | 1][op].mark + rst) % mod;
que[k << 1 | 1][op].sum = (que[k << 1 | 1][op].sum + calcsum(rst, (que[k << 1 | 1][op].r - que[k << 1 | 1][op].l + 1), op)) % mod;
que[k][op].mark = 0;
}
}
void build(int left, int right, int k, int op)
{
que[k][op].l = left;
que[k][op].r = right;
que[k][op].mark = 0;
que[k][op].sum = 0;
if (left == right)
{
que[k][op].sum = 0;
return;
}
imid;
build(lson);
build(rson);
up(k, op);
}
void update(int left, int right, int k, int op, int ql, int qr, ll val)
{
if (qr < left || right < ql)
return;
if (ql <= left && right <= qr)
{
ll st = calcnth(val, left - ql + 1, op);
que[k][op].mark = (que[k][op].mark + st) % mod;
que[k][op].sum = (que[k][op].sum + calcsum(st, que[k][op].r - que[k][op].l + 1, op)) % mod;
return;
}
down(k, op);
imid;
update(lson, ql, qr, val);
update(rson, ql, qr, val);
up(k, op);
}
ll query(int left, int right, int k, int op, int ql, int qr)
{
if (qr < left || right < ql)
return 0;
if (ql <= left && right <= qr)
return que[k][op].sum;
down(k, op);
imid;
return (query(lson, ql, qr) + query(rson, ql, qr)) % mod;
}
int main()
{
init();
int n, m;
sc("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
sc("%lld", &a[i]);
a[i] = (a[i] + a[i - 1]) % mod;
}
build(1, n, 1, 0);
build(1, n, 1, 1);
while (m--)
{
int op, ql, qr;
sc("%d%d%d", &op, &ql, &qr);
if (op == 1)
{
update(1, n, 1, 0, ql, qr, x[0]);
update(1, n, 1, 1, ql, qr, x[1]);
}
else
{
ll ans1 = query(1, n, 1, 0, ql, qr);
ll ans2 = query(1, n, 1, 1, ql, qr);
ll ans = inv_sqrt17 * (ans1 - ans2 + mod) % mod;
ans = (ans + a[qr] - a[ql - 1] + mod) % mod;
pr("%lld\n", ans);
}
}
}
/*
4 2
0 0 0 0
1 1 1
2 1 1
*/