CF633H (线段树维护矩乘 + 莫队)

Fibonacci-ish II

题意:给定一个长度最大为 \(30000\) 的序列,和最多 \(30000\) 个询问,每个询问问某区间 \([L,R]\) 里的数,去掉重复然后排序之后,依次乘上斐波那契数列然后求和,结果对 \(m\) 取余的值。
转化一下题意,也就是在值域数轴上求 \(\sum a_i \cdot b_i\)
\(a_i\) 是当前位置的值,如果在询问区间中存在就为 \(val\),否则为 \(0\)
\(b_i\) 是该位置乘的哪个斐波那契数。

考虑在 \(p\) 位置加入一个数,会产生什么变化。

  • \(a_p = val\)
  • \([p + 1, inf]\) 的所有 \(b\) 变为其下一项。

斐波那契有良好的性质。

\[\left[ \begin{matrix} 1 & 1\\ 1 & 0 \\ \end{matrix} \right]^n = \left[ \begin{matrix} Fib_{n + 1} & Fib_n\\ Fib_n & Fib_{n - 1} \end{matrix} \right] \]

所以操作二转化为 \([p + 1, inf]\) 所有 \(b\) 乘上 \(\left[ \begin{matrix} 1 & 1\\ 1 & 0 \\ \end{matrix} \right]\),用线段树维护。
每个节点的懒标记可以用矩阵维护,但常数太大。
只记录每个懒标记的次数,预处理 \(Fib[i]\)\(invFiv[i]\) 来分别表示 \(\left[ \begin{matrix} 1 & 1\\ 1 & 0 \\ \end{matrix} \right]^i\) 和其逆 \(\left[ \begin{matrix} 0 & 1\\ 1 & -1 \\ \end{matrix} \right]^i\)
为了减小常数,这里我用一维数组来实现矩阵。

#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); ++ i)
#define per(i, a, b) for(int i = (a); i >= (b); -- i)
#define pb emplace_back
#define All(X) X.begin(), X.end()
using namespace std;
using ll = long long;
constexpr int N = 3e4 + 5, B = 175;

int n, m, P, Fib[N][4] = {1, 0, 0, 1}, invFib[N][4] = {1, 0, 0, 1};
int M[4] = {1, 1, 1, 0};
int invM[4] = {0, 1, 1, -1};

int c[4];

inline void mul(int a[4], int b[4]) {
	c[0] = (a[0] * b[0] + a[1] * b[2]) % P;
	c[1] = (a[0] * b[1] + a[1] * b[3]) % P;
	c[2] = (a[2] * b[0] + a[3] * b[2]) % P;
	c[3] = (a[3] * b[3] + a[2] * b[1]) % P;
	memcpy(a, c, 16);
}

int a[N], b[N], tot;
int t[N << 2][4], val[N << 2], tag[N << 2];

#define ls x << 1
#define rs ls | 1

inline void pushup(int x) {
	t[x][0] = (t[ls][0] * val[ls] + t[rs][0] * val[rs]) % P;
	t[x][1] = (t[ls][1] * val[ls] + t[rs][1] * val[rs]) % P;
	t[x][2] = (t[ls][2] * val[ls] + t[rs][2] * val[rs]) % P;
	t[x][3] = (t[ls][3] * val[ls] + t[rs][3] * val[rs]) % P;
}
/*
由于 val 可能为 0,直接在叶子节点上操作有可能会丢失信息
因此把乘 val 的操作放在 pushup 中完成
*/

inline void pushdown(int x) {
	if(tag[x]) {
		if(tag[x] > 0) {
			mul(t[ls], Fib[tag[x]]);
			mul(t[rs], Fib[tag[x]]);
		}
		if(tag[x] < 0) {
			mul(t[ls], invFib[-tag[x]]);
			mul(t[rs], invFib[-tag[x]]);
		}
		tag[ls] += tag[x];
		tag[rs] += tag[x];
		tag[x] = 0;
	}
}

void build(int x = 1, int l = 1, int r = tot) {
	val[x] = l != r;  //非叶子节点的 val 为 1
	if(l == r) {
		memcpy(t[x], M, 16);
		return;
	}
	int mid = l + r >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
}

inline void insert(int p, int x = 1, int l = 1, int r = tot) {
	if(l == r) return val[x] = b[p], void();
	pushdown(x);
	int mid = l + r >> 1;
	if(p <= mid) {
		insert(p, ls, l, mid);
		mul(t[rs], M);
		++ tag[rs];
	}
	else insert(p, rs, mid + 1, r);
	pushup(x);
}

inline void erase(int p, int x = 1, int l = 1, int r = tot) {
	if(l == r) return val[x] = 0, void();
	pushdown(x);
	int mid = l + r >> 1;
	if(p <= mid) {
		erase(p, ls, l, mid);
		mul(t[rs], invM);
		-- tag[rs];
	}
	else erase(p, rs, mid + 1, r);
	pushup(x);
}

int cnt[N], ans[N];

inline void add(int x) {
	if(++ cnt[x] == 1) insert(x);
}

inline void del(int x) {
	if(-- cnt[x] == 0) erase(x);
}

#define c(x) ((x) / B) 

struct qry {
	int l, r, id;
	bool operator < (const qry &x) {
		if(c(l) != c(x.l)) return c(l) < c(x.l);
		if(c(l) & 1) return c(r) < c(x.r);
		return c(r) > c(x.r);
	}
} q[N];

void init_Fib() {
	rep(i, 1, n) {
		memcpy(Fib[i], Fib[i - 1], 16);
		memcpy(invFib[i], invFib[i - 1], 16);
		mul(Fib[i], M);
		mul(invFib[i], invM);
	}
}

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	cin >> n >> P;
	invM[3] = P - 1;
	init_Fib();
	rep(i, 1, n) {
		cin >> a[i];
		b[i] = a[i];
	}
	sort(b + 1, b + n + 1);
	tot = unique(b + 1, b + n + 1) - b - 1;
	rep(i, 1, n) {
		a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
	}
	rep(i, 1, tot) {
		b[i] = (b[i] % P + P) % P;
	}
	cin >> m;
	rep(i, 1, m) {
		cin >> q[i].l >> q[i].r;
		q[i].id = i;
	}
	sort(q + 1, q + m + 1);
	build();
	int l = 1, r = 0;
	rep(i, 1, m) {
		auto[L, R, id] = q[i];
		while(l < L) del(a[l ++]);
		while(l > L) add(a[-- l]);
		while(r < R) add(a[++ r]);
		while(r > R) del(a[r --]);
		ans[id] = t[1][2] % P;
	}
	rep(i, 1, m) cout << ans[i] << '\n';
	return 0;
}
posted @ 2024-02-05 14:28  Lu_xZ  阅读(8)  评论(0编辑  收藏  举报