首页 > 代码库 > [补档计划] 树6 - 莫队算法

[补档计划] 树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;
}
View Code

 

[补档计划] 树6 - 莫队算法