首页 > 其他分享 >CF446C DZY Loves Fibonacci Numbers 题解和加强

CF446C DZY Loves Fibonacci Numbers 题解和加强

时间:2023-02-14 08:11:45浏览次数:59  
标签:CF446C int 题解 sum sf2 sf1 Fibonacci tg 那契

简要题意

https://www.luogu.com.cn/problem/CF446C

给定一个长度为 \(n\) 的序列 \(A\),要求支持两种操作:1 给定区间 \((l, r)\) 对这个区间内的每个数,依次加斐波那契数列的前 \(r - l + 1\) 项;2 给定区间求和。
数据范围:\(1\le n \le 3\times10^5, 1 \le q \le 3\times 10^5\)。

加强:给定区间求平方和。

原题思路

斐波那契数列的差分、前缀和还是斐波那契数列,和区间加一次函数、二次函数等有本质区别,经典的差分、前缀和等处理以后数据结构维护的简单思路是行不通的。必须另辟蹊径。

根据斐波那契数列的递推式可以得到它的一个性质:两个斐波那契数列错开一位,相减以后得到的也是一个斐波那契数列,并且这个斐波那契数列相对于原先两个,又向前移动一位,如下所示:

\[\begin{aligned} &F_1: \{1, 1, 2, 3, 5, 8, 13,\cdots\}\\ &F_2: \{0, 1, 1, 2, 3, 5, 8,\cdots\}\\ &F_3: \{1, 0, 1, 1, 2, 3, 5,\cdots\}\\ &F_4: \{-1, 1, 0, 1, 1, 2, 3,\cdots\}\\ \end{aligned} \]

注意到 \(\forall i > 2, F_{i, j} = F_{i - 2, j} - F_{i - 1, j}\),于是所有的 \(F\) 的每一行都可以表示为 \(F_1, F_2\) 的线性组合,且系数可以在 \(\Theta(n)\) 时间递推预处理得到。

这些数组可以帮助我们进行区间加的操作。首先考虑另一个经典模型:给定序列 \(A, B\),\(B\) 始终不变,要求支持一种操作给定 \(l, r\),使 \(\forall l\le i\le r, A_i \leftarrow A_i + kB_i\),维护 \(A\) 的区间和。这直接在线段树上令 tag 是 \(B\) 的系数即可,对于下推标记和区间更新,通过预处理 \(B\) 的前缀和即可做到。如果不止有一个数组,也是一个道理,因为两个数组互不影响。

时间/空间复杂度 \(\Theta(n \log n)/\Theta(n)\)。

原题代码

// Author: kyEEcccccc

#include <bits/stdc++.h>

using namespace std;

using LL = long long;
using ULL = unsigned long long;

#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)

const int N = 300005, P = 1000000009;

int sf1[N], sf2[N];

struct SegTree
{
	int sum[N << 2];
	pair<int, int> tg[N << 2];

	void update(int p)
	{
		sum[p] = (sum[p * 2] + sum[p * 2 + 1]) % P;
	}

	void pushdown(int p, int cl, int cr)
	{
		int cm = (cl + cr) >> 1;
		LL t1 = tg[p].first, t2 = tg[p].second;
		tg[p] = {0, 0};
		sum[p * 2] = (sum[p * 2] + t1 * (sf1[cm] - sf1[cl - 1] + P)) % P;
		sum[p * 2] = (sum[p * 2] + t2 * (sf2[cm] - sf2[cl - 1] + P)) % P;
		sum[p * 2 + 1] = (sum[p * 2 + 1] + t1 * (sf1[cr] - sf1[cm] + P)) % P;
		sum[p * 2 + 1] = (sum[p * 2 + 1] + t2 * (sf2[cr] - sf2[cm] + P)) % P;
		(tg[p * 2].first += t1) %= P;
		(tg[p * 2].second += t2) %= P;
		(tg[p * 2 + 1].first += t1) %= P;
		(tg[p * 2 + 1].second += t2) %= P;
	}

	void add(int p, int cl, int cr, int l, int r, LL x1, LL x2)
	{
		if (cl >= l && cr <= r)
		{
			sum[p] = (sum[p] + x1 * (sf1[cr] - sf1[cl - 1] + P)
				+ x2 * (sf2[cr] - sf2[cl - 1] + P)) % P;
			(tg[p].first += x1) %= P;
			(tg[p].second += x2) %= P;
			return;
		}
		pushdown(p, cl, cr);
		int cm = (cl + cr) >> 1;
		if (l <= cm)
		{
			add(p * 2, cl, cm, l, r, x1, x2);
		}
		if (r > cm)
		{
			add(p * 2 + 1, cm + 1, cr, l, r, x1, x2);
		}
		update(p);
	}

	LL get_sum(int p, int cl, int cr, int l, int r)
	{
		if (cl >= l && cr <= r)
		{
			return sum[p];
		}
		pushdown(p, cl, cr);
		int cm = (cl + cr) >> 1;
		LL ret = 0;
		if (l <= cm)
		{
			ret += get_sum(p * 2, cl, cm, l, r);
		}
		if (r > cm)
		{
			ret += get_sum(p * 2 + 1, cm + 1, cr, l, r);
		}
		return ret % P;
	}
} segt;

int n, m, a[N];
pair<int, int> g[N];

int main(void)
{
	// freopen(".in", "r", stdin);
	// freopen(".out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(nullptr);

	cin >> n >> m;
	F(i, 1, n)
	{
		cin >> a[i];
		(a[i] += a[i - 1]) %= P;
	}

	g[1] = {1, 0};
	g[2] = {0, 1};
	F(i, 3, n)
	{
		g[i].first = (g[i - 2].first - g[i - 1].first + P) % P;
		g[i].second = (g[i - 2].second - g[i - 1].second + P) % P;
	}

	sf1[1] = sf1[2] = 1;
	sf2[1] = 0, sf2[2] = 1;
	F(i, 3, n)
	{
		sf1[i] = (sf1[i - 1] + sf1[i - 2]) % P;
		sf2[i] = (sf2[i - 1] + sf2[i - 2]) % P;
	}
	F(i, 1, n)
	{
		sf1[i] = (sf1[i - 1] + sf1[i]) % P;
		sf2[i] = (sf2[i - 1] + sf2[i]) % P;
	}

	F(i, 1, m)
	{
		int op, l, r;
		cin >> op >> l >> r;
		if (op == 1)
		{
			segt.add(1, 1, n, l, r, g[l].first, g[l].second);
		}
		else
		{
			cout << (segt.get_sum(1, 1, n, l, r)
				+ a[r] - a[l - 1] + P) % P << '\n';
		}
	}

	return 0;
}

加强版思路

通过上文对原题的分析,我们可以将区间加斐波那契数列的前若干项,变为加两个斐波那契数列的对应位置项。对于维护平方,也是一个道理:

\[\begin{aligned} \sum_{i=l}^{r}(A_i + kB_i)^2 &= \sum A_i^2 + k^2\sum B_i^2 + k\sum A_iB_i \end{aligned} \]

那么我们只需要预处理 \(B_i^2\) 前缀和,同时维护 \(A_iB_i\) 的区间和即可,又注意到:

\[\begin{aligned} \sum_{i=l}^r(A_i + kB_i)B_i &= \sum A_iB_i + k\sum B_i^2\\ \sum_{i=l}^r(A_i + kB_i)C_i &= \sum A_iC_i + k\sum B_iC_i \end{aligned} \]

而 \(B_iC_i\) 的前缀和也可以预处理,所以加强版问题在这个转化视角下也是容易的。

标签:CF446C,int,题解,sum,sf2,sf1,Fibonacci,tg,那契
From: https://www.cnblogs.com/kyeecccccc/p/17118244.html

相关文章

  • str 学数学 题解
    Example1(str学数学)str同学因为名字里含有一个str,所以觉得字符串对于他来说太简单了,于是他开始了他的数学之旅。在旅途中str遇到了刚抽到胡桃的lyl,而lyl同学正......
  • clion 2022 win10上编译的程序不可运行---问题解决
    会报错,0xc000007b网上说,可能是dll库丢失等问题,我试了无效我的修复过程如下编译的时候,默认是使用ninja,将buildtool改成make即可 ......
  • CF1567E Non-Decreasing Dilemma 题解 线段树
    题目链接:http://codeforces.com/problemset/problem/1567/E题目大意:有一个长度为\(n\)的数列\(a\),你需要对其进行\(q\)次操作,操作有两种类型,按如下格式给出:1xy:......
  • Ubuntu cmake 安装以及问题解决(muduo库编译)
    1、安装cmakesudoapt-getinstallcmake 2、安装之后查看是否安装成功:cmake--version3、出现 NoCMAKE_C_COMPILERcouldbefound.如何解决使用cmake命令时发......
  • [ABC289D] Step Up Robot 题解
    Problem有一机器人初始在\(0\)级阶梯,对于每一级阶梯,机器人可以从\(1\simN\)种任选一个\(i\)走\(A_i\)步,同时有\(M\)个障碍在\(B_i\)级阶梯,若走到障碍则将无......
  • P2430 严酷的训练 题解
    题目背景Lj的朋友WKY是一名神奇的少年,在同龄人之中有着极高的地位。。。题目描述他的老师老王对他的程序水平赞叹不已,于是下决心培养这名小子。老王的训练方式很奇怪,他......
  • 前端导出pdf字体表格被截断问题解决
    最近有个导出pdf的需求,写好之后分页出现字体,表格被截断的问题,影响美观,需要解决。  经过多方查找,发现一个比较好的思路,设置背景色为白色,然后转成图片后,获取截断处图片......
  • ABC283E 题解
    前言题目传送门!更好的阅读体验?很简单的一道题,强行在英语课的时候想到做法。存储方式与其他题解稍有不同。本题解着重讲是怎么想到这个做法的。思路首先考虑暴力。用......
  • 【题解】CF1500B Two chandeliers
    题目分析:感觉这个题难度虚高,怕是因为一般不用翻译软件翻译输入输出格式(如果我们关注到了输入格式的话就可以发现一个很有用的地方,就是\(a\)和\(b\)单个序列内包含的......
  • 【题解】ABC289 E-G
    现在ABC的G竟然沦落到我都能做出来的地步了。E.SwapPlaces题目分析:这个题初看可能不很好好做的样子,但是看到它的数据范围是个\(O(n^2)\)随便跑的复杂度,所以直接......