[补档计划] 树6 - 莫队算法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[补档计划] 树6 - 莫队算法相关的知识,希望对你有一定的参考价值。
[CF633H] Fibonacci-ish II
题意
给定长度为 $N$ 个序列 $A = (a_1, a_2, ..., a_N)$ .
$M$ 组询问 $(l, r)$ : 将 $a_l, a_{l+1}, ..., a_r$ 提取出来, 排序, 去重, 得到长度为 $K$ 的序列 $B = (b_1, b_2, ..., b_K)$ , 求 $\sum_{i = 1}^K f_ib_i$ .
其中 $f_i$ 为斐波那契数列的第 $i$ 项: $f_0 = f_1 = 1, f_i = f_{i-1} + f_{i-2}$ .
$N \le 30000$ .
分析
莫队算法 + 线段树.
信息合并: 记录区间的 $\sum_{i = 1}^K f_ib_i$ 和 $\sum_{i = 1}^K f_{i+1}b_i$ , 预处理转移矩阵的 $1$ 到 $n$ 次幂, 那么可以直接合并.
实现
#include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <cmath> #include <algorithm> using namespace std; #define F(i, a, b) for (register int i = (a); i <= (b); i++) const int N = 35000; const int S = 70000; //65536 int n, m, a[N], h[N]; int f[N]; struct Matrix { int val[2][2]; inline Matrix(void) { memset(val, 0, sizeof val); } friend inline Matrix operator * (Matrix A, Matrix B) { Matrix C; F(i, 0, 1) F(j, 0, 1) F(k, 0, 1) (C.val[i][j] += A.val[i][k] * B.val[k][j]) %= m; return C; } }back[N]; int q, c, ans[N]; struct Query { int l, r, id; friend bool inline operator < (Query A, Query B) { int ownA = A.l / c, ownB = B.l / c; return ownA != ownB ? ownA < ownB : A.r < B.r; } }qs[N]; int cnt[N]; int D, siz[S], sum[S][2]; namespace Input { const int S = 2000000; char s[S], *h = s+S, *t = h; inline char getchr(void) { if (h == t) fread(s, 1, S, stdin), h = s; return *h++; } inline int rd(void) { int f = 1; char c = getchr(); for (; !isdigit(c); c = getchr()) if (c == ‘-‘) f = -1; int x = 0; for (; isdigit(c); c = getchr()) x = x*10+c-‘0‘; return x*f; } } using Input::rd; inline void Modify(int x, int c) { int fac = h[x]; x += (1<<D), siz[x] = c, sum[x][0] = sum[x][1] = c * fac; for (x >>= 1; x > 0; x >>= 1) { int S = siz[x<<1]; siz[x] = S + siz[x<<1|1]; sum[x][0] = (sum[x<<1][0] + back[S].val[0][0] * sum[x<<1|1][0] + back[S].val[0][1] * sum[x<<1|1][1]) % m; sum[x][1] = (sum[x<<1][1] + back[S].val[1][0] * sum[x<<1|1][0] + back[S].val[1][1] * sum[x<<1|1][1]) % m; } } inline void Add(int w) { if (++cnt[w] == 1) Modify(w, 1); } inline void Erase(int w) { if (cnt[w]-- == 1) Modify(w, 0); } int main(void) { #ifndef ONLINE_JUDGE freopen("xsy2108.in", "r", stdin); freopen("xsy2108.out", "w", stdout); #endif n = rd(), m = rd(); F(i, 1, n) a[i] = h[i] = rd(); sort(h+1, h+n+1); F(i, 1, n) a[i] = lower_bound(h+1, h+n+1, a[i]) - h; F(i, 1, n) h[i] %= m; f[1] = f[2] = 1; F(i, 3, n+5) f[i] = (f[i-1] + f[i-2]) % m; back[0].val[0][0] = back[0].val[1][1] = 1; back[1].val[0][1] = back[1].val[1][0] = back[1].val[1][1] = 1; F(i, 2, n) back[i] = back[i-1] * back[1]; q = rd(), c = (int)sqrt(n); F(i, 1, q) { int l = rd(), r = rd(); qs[i] = (Query){l, r, i}; } sort(qs+1, qs+q+1); int ml = 1, mr = 0; for (D = 0; (1 << D) - 1 < n; D++); // 无需用到开区间, 不用 +1 F(i, 1, q) { while (mr < qs[i].r) Add(a[++mr]); while (ml > qs[i].l) Add(a[--ml]); while (mr > qs[i].r) Erase(a[mr--]); while (ml < qs[i].l) Erase(a[ml++]); ans[qs[i].id] = sum[1][0]; } F(i, 1, q) printf("%d\n", ans[i]); return 0; }
以上是关于[补档计划] 树6 - 莫队算法的主要内容,如果未能解决你的问题,请参考以下文章