環鴿的數列(數列的通項,區間加等比數列,區間求和)

題意:

有一個遞推數列 \{F_n\} ,F_1=1,\ F_2=3,\ F_n=3F_{n-1}+2F_{n-2} (n>2)

對於一個長度爲 n 的區間,共有 m 次操作,每個操作給三個數字 op,l,r,你需要支持下面兩種操作

op=1:區間 [l,\ r] 每個位置加上 F_{i-l+1}

op=2:輸出區間 [l,\ r] 的和膜 998244353 的值


首先肯定要找到遞推數列 \{F_n\} 的性質,顯然直接在線段樹上打標記是不行的,那麼我們可以考慮先求出遞推數列 \{F_n\} 的通項

首先假設 遞推數列 \{F_n\} 的遞推式爲 F_n=aF_{n-1}+bF_{n-2} (n>2), 並且存在 s, r 滿足 F_n-rF_{n-1}=s(F_{n-1}-rF_{n-2}),將這兩個式子聯立得到 ,可以求出 s, r = \frac{a\pm \sqrt{a^2+4b}}{2}

若 s=r

則 s=r=\frac{a}{2},將 s=r 代入 F_n-rF_{n-1}=s(F_{n-1}-rF_{n-2}) ,並且遞推數列 \{F_n-F_{n-1}\} 是等比數列

F_n-rF_{n-1}=r(F_{n-1}-rF_{n-2}),繼續化簡得到 F_n=rF_{n-1}+r^{n-2}(F_{2}-rF_{1})

將 F_{n-1}=rF_{n-2}+r^{n-3}(F_2-rF_1)  代入上述式子得到 F_n=r^2F_{n-2}+2r^{n-2}(F_2-rF_1)

同理可以得到 F_n=r^{n-3}F_{3}+(n-3)r^{n-2}(F_2-rF_1)

將 F_3=rF_2+r(F_2-rF_1)=2rF_2-r^2F_1 代入上述式子得到

F_n=r^{n-3}*(2rF_2-r^2F_1)+(n-3)r^{n-2}*(F_2-rF_1)

化簡得到 F_n=(n-1)F_2r^{n-2}-(n-2)F_1r^{n-1}

若 s\neq r

將 s,r 代入 F_n-rF_{n-1}=s(F_{n-1}-rF_{n-2}) 得到兩個式子,並且遞推數列 \{F_n-F_{n-1}\} 是等比數列

F_n-rF_{n-1}=s(F_{n-1}-rF_{n-2}),繼續化簡得到 F_n-rF_{n-1}=s^{n-2}(F_{2}-rF_{1})

F_n-sF_{n-1}=r(F_{n-1}-sF_{n-2}),繼續化簡得到 F_n-sF_{n-1}=r^{n-2}(F_{2}-sF_{1})

將 F_{n-1} 消掉得到 F_n=\frac{(F_2-rF_1)*s^{n-1}-(F_2-sF_1)*r^{n-1}}{s-r}

回到這個題目,首先得到 s,r=\frac{3\pm \sqrt{17}}{2},然後得到通項 F_n=\frac{1}{\sqrt{17}}((\frac{3+\sqrt{17}}{2})^n-(\frac{3-\sqrt{17}}{2})^n)

首先用二次剩餘和逆元將式子中出現的根號和分數化爲整數。

所以題目中的兩個操作就變成了區間加等比數列,區間求和,然後使用兩個線段樹分別維護兩個次方即可

Code:

// 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 <bits/stdc++.h>
#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
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章