传送门


这题除了暴力踩标程和正解卡常数以外是道很好的题目

首先看到我们要求的东西与\(Fibonacci\)有关,考虑矩阵乘法进行维护。又看到\(n \leq 30000\),这告诉我们正解算法其实比较暴力,又因为直接在线解决看起来就比较麻烦,所以考虑离线询问,莫队解决。

我们设斐波那契数列的转移矩阵为\(T = \left( \begin{array}{ccc} 0 & 1 \\ 1 & 1 \end{array} \right)\)

先将\(a\)离散化,用一棵线段树维护矩阵运算。那么我们需要支持的是:插入一个数并使比它大的数对应的\(Fibonacci\)数向后移一个。这个可以在线段树的对应节点打上一个\(T\)的标记,表示向右转移一个,经过这个节点时pushdown下去。删除一个数就打上它的逆矩阵的标记。总复杂度为\(O(n\sqrt{n}logn)\)

Tips:如果你TLE在了第35个点,请尽力卡常,简化取模过程、避免不必要运算(详见代码中pushup过程)

#include<bits/stdc++.h>
//This code is written by Itst
#define lch (x << 1)
#define rch (x << 1 | 1)
#define mid ((l + r) >> 1)
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c)){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    while(isdigit(c)){
        a = (a << 3) + (a << 1) + (c ^ '0');
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 3e4 + 7;
int step = 0 , N , M , Q , T , cnt , num[MAXN] , lsh[MAXN] , times[MAXN] , ans[MAXN];
struct query{
    int ind , l , r;
    bool operator <(const query a)const{
        return l / T == a.l / T ? ((l / T) & 1 ? r > a.r : r < a.r) : l < a.l;
    }
}now[MAXN];
struct matrix{
    int a[2][2];
    int* operator [](int x){return a[x];}
    matrix(bool f = 1){if(f) memset(a , 0 , sizeof(a));}
    matrix operator *(matrix b){
        matrix c;
        for(int i = 0 ; i < 2 ; ++i)
            for(int j = 0 ; j < 2 ; ++j)
                for(int k = 0 ; k < 2 ; ++k)
                    c[i][j] += a[i][k] * b[k][j];
        for(int i = 0 ; i < 2 ; ++i)
            for(int j = 0 ; j < 2 ; ++j)
                c[i][j] %= M;
        return c;
    }
    matrix operator *(int b){
        matrix c(0);
        for(int i = 0 ; i < 2 ; ++i)
            for(int j = 0 ; j < 2 ; ++j)
                c[i][j] = a[i][j] * b;
        return c;
    }
    matrix operator +(matrix b){
        matrix c(0);
        for(int i = 0 ; i < 2 ; ++i)
            for(int j = 0 ; j < 2 ; ++j)
                c[i][j] = (a[i][j] + b[i][j]) % M;
        return c;
    }
    bool operator ==(matrix b){
        for(int i = 0 ; i < 2 ; ++i)
            for(int j = 0 ; j < 2 ; ++j)
                if(a[i][j] != b[i][j])
                    return 0;
        return 1;
    }
    bool operator !=(matrix b){
        return !(*this == b);
    }
}F , E , G , a , b;
struct node{
    matrix ans , mark;
    int times;
}Tree[MAXN << 2];

inline void mark(int x , const matrix mark){
    Tree[x].mark = Tree[x].mark * mark;
    Tree[x].ans = Tree[x].ans * mark;
}

inline void pushdown(int x){
    if(Tree[x].mark != E){
        mark(lch , Tree[x].mark);
        mark(rch , Tree[x].mark);
        Tree[x].mark = E;
    }
}

inline void pushup(int x){
    a = Tree[lch].ans;
    b = Tree[rch].ans;
    if(Tree[lch].times != 1)
        a = a * Tree[lch].times;
    if(Tree[rch].times != 1)
        b = b * Tree[rch].times;
    Tree[x].ans = a + b;
}

void insert(int x , int l , int r , int tar){
    if(l == r){
        Tree[x].times = lsh[tar];
        return;
    }
    pushdown(x);
    if(mid >= tar){
        insert(lch , l , mid , tar);
        mark(rch , F);
    }
    else
        insert(rch , mid + 1 , r , tar);
    pushup(x);
}

void erase(int x , int l , int r , int tar){
    if(l == r){
        Tree[x].times = 0;
        return;
    }
    pushdown(x);
    if(mid >= tar){
        erase(lch , l , mid , tar);
        mark(rch , G);
    }
    else
        erase(rch , mid + 1 , r , tar);
    pushup(x);
}

void init(int x , int l , int r){
    Tree[x].times = l != r;
    Tree[x].mark = E;
    if(l != r){
        init(lch , l , mid);
        init(rch , mid + 1 , r);
    }
    else
        Tree[x].ans = F;
}

inline void add(int a){
    if(!times[a]++)
        insert(1 , 1 , cnt , a);
    ++step;
}

inline void del(int a){
    if(!--times[a])
        erase(1 , 1 , cnt , a);
    ++step;
}

int main(){
    N = read();
    M = read();
    T = sqrt(N);
    E[0][0] = E[1][1] = F[0][1] = F[1][0] = F[1][1] = G[1][0] = G[0][1] = 1;
    G[0][0] = M - 1;
    for(int i = 1 ; i <= N ; ++i)
        num[i] = lsh[i] = read();
    sort(lsh + 1 , lsh + N + 1);
    cnt = unique(lsh + 1 , lsh + N + 1) - lsh - 1;
    for(int i = 1 ; i <= N ; ++i)
        num[i] = lower_bound(lsh + 1 , lsh + cnt + 1 , num[i]) - lsh;
    for(int i = 1 ; i <= cnt ; ++i)
        lsh[i] %= M;
    Q = read();
    for(int i = 1 ; i <= Q ; ++i){
        now[i].ind = i;
        now[i].l = read();
        now[i].r = read();
    }
    sort(now + 1 , now + Q + 1);
    int L = 1 , R = 0;
    init(1 , 1 , cnt);
    for(int i = 1 ; i <= Q ; ++i){
        while(R < now[i].r)
            add(num[++R]);
        while(L > now[i].l)
            add(num[--L]);
        while(R > now[i].r)
            del(num[R--]);
        while(L < now[i].l)
            del(num[L++]);
        ans[now[i].ind] = Tree[1].ans[0][1] * Tree[1].times % M;
    }
    cerr << step << endl;
    for(int i = 1 ; i <= Q ; ++i)
        printf("%d\n" , ans[i]);
    return 0;
}

CF633H Fibonacci-ish II 莫队、线段树、矩阵乘法的更多相关文章

  1. 【CF633H】Fibonacci-ish II 莫队+线段树

    [CF633H]Fibonacci-ish II 题意:给你一个长度为n的序列$a_i$.m个询问,每个询问形如l,r:将[l,r]中的所有$a_i$排序并去重,设得到的新数列为$b_i$,求$b_1 ...

  2. Manthan, Codefest 16 H. Fibonacci-ish II 大力出奇迹 莫队 线段树 矩阵

    H. Fibonacci-ish II 题目连接: http://codeforces.com/contest/633/problem/H Description Yash is finally ti ...

  3. Codeforces 666E E - Forensic Examination SA + 莫队 + 线段树

    E - Forensic Examination 我也不知道为什么这个复杂度能过, 而且跑得还挺快, 数据比较水? 在sa上二分出上下界, 然后莫队 + 线段树维护区间众数. #include< ...

  4. 洛谷P3246 序列 [HNOI2016] 莫队/线段树+扫描线

    正解:莫队/线段树+扫描线 解题报告: 传送门! 似乎是有两种方法的,,,所以分别港下好了QAQ 第一种,莫队 看到这种询问很多区间之类的就会自然而然地想到莫队趴?然后仔细思考一下,发现复杂度似乎是欧 ...

  5. [hdoj6483][莫队+线段树/ST]

    A Sequence Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  6. [bzoj4358]permu:莫队+线段树/回滚莫队

    这道题是几天前水过去的,现在快没印象了,水一发. 首先我们看到它让求解的是最长的值域 连续段长度,很好. 然后就想到了山海经,但但是我还没有做. 然后又想到了很久以前的一次考试的T3旅馆hotel(我 ...

  7. BZOJ 4129 树上带修莫队+线段树

    思路: 可以先做做BZOJ3585 是序列上的mex 考虑莫队的转移 如果当前数字出现过 线段树上把它置成1 对于询问 二分ans 线段树上查 0到ans的和 是不是ans+1 本题就是把它搞到了序列 ...

  8. 【Codeforces718C】Sasha and Array 线段树 + 矩阵乘法

    C. Sasha and Array time limit per test:5 seconds memory limit per test:256 megabytes input:standard ...

  9. LOJ2980 THUSC2017大魔法师(线段树+矩阵乘法)

    线段树每个节点维护(A,B,C,len)向量,操作即是将其乘上一个矩阵. #include<iostream> #include<cstdio> #include<cma ...

  10. hdu 5068(线段树+矩阵乘法)

    矩阵乘法来进行所有路径的运算, 线段树来查询修改. 关键还是矩阵乘法的结合律. Harry And Math Teacher Time Limit: 5000/3000 MS (Java/Others ...

随机推荐

  1. 【Java入门提高篇】Day28 Java容器类详解(十)LinkedHashMap详解

    今天来介绍一下容器类中的另一个哈希表———>LinkedHashMap.这是HashMap的关门弟子,直接继承了HashMap的衣钵,所以拥有HashMap的全部特性,并青出于蓝而胜于蓝,有着一 ...

  2. (网页)jQuery UI 实例 - 日期选择器(Datepicker)

    默认功能 日期选择器(Datepicker)绑定到一个标准的表单 input 字段上.把焦点移到 input 上(点击或者使用 tab 键),在一个小的覆盖层上打开一个交互日历.选择一个日期,点击页面 ...

  3. GridSearchCV交叉验证

    代码实现(基于逻辑回归算法): # -*- coding: utf-8 -*- """ Created on Sat Sep 1 11:54:48 2018 @autho ...

  4. C#使用Aforge调用摄像头拍照

    一. 新建一个Winform项目 二.使用Nuget添加引用 安装下图中红色框住的两个程序包 安装完后发现安装了如下图的程序包,这是因为上述两个程序包存在对其它程序包的依赖. 三.编写程序 1. 窗体 ...

  5. HDU ACM 1856 More is better(并查集)

    [题目链接]http://acm.hdu.edu.cn/showproblem.php?pid=1856 [解题思路]给的数据有点大,干脆少开点数组,直接上set存储有朋友的孩子的编号,同时根据编号初 ...

  6. 修改css的(屏蔽)overflow: hidden;实现浏览器能把网页全图保存成图片

    摘要: 1.项目需要,需要对网页内容“下载”保存成全图片 2.QQ浏览器等主流浏览器都支持这种下载保存功能 3.项目需要场景:编写好的项目维护文档,放在服务器上.如果是txt不能带图片可视化,如果wo ...

  7. 3.8Python数据处理篇之Numpy系列(八)---Numpy的梯度函数

    目录 目录 前言 (一)函数说明 (二)一维数组的应用 (三)多维数组的应用 目录 前言 梯度函数,其中的梯度也就是斜率,反映的是各个数据的变化率.在numpy中只有一个梯度函数. (一)函数说明 ( ...

  8. ccf题库中2016年4月2日俄罗斯方块问题

    题目如下: 问题描述 俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏. 游戏在一个15行10列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者没有放置方块.每一轮,都会有一个新 ...

  9. The resource configuration is not modifiable in this context.

    项目中使用了Jersey RESTful 框架, 更新代码后服务能正常起来, 在页面登录时验证码不显示 后台报错 java.lang.IllegalStateException: The resour ...

  10. java用星星符号打印出一个直角三角形

    package debug; public class Demo10 { public static void main(String[] args) { //用星星符号打印出一个直角三角形 for( ...