1. 程式人生 > >[補檔計劃] 樹6 - 莫隊算法

[補檔計劃] 樹6 - 莫隊算法

pre type cci == dig isp main gis 莫隊

[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; }
View Code

[補檔計劃] 樹6 - 莫隊算法