CodeForces - 633 H Fibonacci-ish II(莫队+线段树)

题意:给你一个长为n的序列,给你一个mod,再给你q次询问,每次区间查询P(a) = aF1 + aF2 + ... + an·Fn         F为对应的斐波那契数列,a1为以排好序且不重复的区间内的数

参考博客:https://blog.csdn.net/zearot/article/details/50850792

 

题解:首先对这道题目我们能够想到用莫队处理区间的查询,那么我们只要想办法把莫队的左右区间扩展和收缩解决就可以,那么怎么解决下一个出现的ai是第几大,以及比ai要大的数后移(或前移)的操作就需要用到线段树的维护,区间维护要移动的距离,正数表示向右,负数表示向左。

 

斐波那契的一个公式F[i-1]*F[k]+F[i]*F[k+1]=F[i+k],所以只要每次维护两个值,一个是第一行的值,一个是第二行的值,线段树维护要移动的距离,

(图是我盗的,盗自上面的博客)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <cstring>
#include <iomanip>
#include <set>
#include<ctime>
#include<unordered_map>
//CLOCKS_PER_SEC
#define se second
#define fi first
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define Pii pair<int,int>
#define Pli pair<ll,int>
#define ull unsigned long long
#define pb push_back
#define fio ios::sync_with_stdio(false);cin.tie(0)
const int N=3e4+10;
const ull base=163;
const int INF=0x3f3f3f3f;
using namespace std;
int a[N];
struct node {
    int l,r,id;
}Q[N];
int pos[N];
int L=1,R=0;
int ans=0;
int res[N];
int n,q,m;
int mm=1;//离散化后的个数
int fib[N<<1];
int rnk[N];//记录去重后并且排序的a[i]
int rnkA[N];//记录每个a[i]的第几大
int mp[N];//记录区间内有多少个a[i]
int V[N<<2],VL[N<<2],S[N<<2];//S是记录了区间要移动的长度 正数为向右,负数为向左
bool cmp(node x,node y){
    if(pos[x.l]==pos[y.l])return x.r<y.r;
    return pos[x.l]<pos[y.l];
}
void init(){//离散化
    sort(rnk+1,rnk+1+n);
    for(int i=2;i<=n;i++){//去重
        if(rnk[i-1]!=rnk[i]){
            rnk[++mm]=rnk[i];
        }
    }
    for(int i=1;i<=n;i++){//离散后为得到每个a[i]是第几大而进行二分
        int l=1,r=mm,ans = 0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(rnk[mid]<=a[i]){
                l=mid+1;
                ans=mid;
            }
            else r=mid-1;
        }
        rnkA[i]=ans;
    }
}
//接下来所有的操作都是在1--mm这个区间内进行线段树操作
void shift(int rt,int s){
    int l=(VL[rt]*fib[mm+s]+V[rt]*fib[mm+s+1])%m;
    int r=(VL[rt]*fib[mm+s-1]+V[rt]*fib[mm+s])%m;
    V[rt]=l;
    VL[rt]=r;
}
void pushup(int rt){
    V[rt]=(V[rt<<1]+V[rt<<1|1])%m;
    VL[rt]=(VL[rt<<1]+VL[rt<<1|1])%m;
}

void pushdown(int rt){
    if(S[rt]){
        S[rt<<1]+=S[rt];
        S[rt<<1|1]+=S[rt];
        shift(rt<<1,S[rt]);
        shift(rt<<1|1,S[rt]);
        S[rt]=0;
    }
}

void move(int x,int l,int r,int rt){
    if(l==r){
        V[rt]=VL[rt]=0; 
        return ;
    }
    pushdown(rt);
    int m=(l+r)>>1;
    if(x<=m){
        move(x,lson);
        S[rt<<1|1]--;
        shift(rt<<1|1,-1);
    }
    else move(x,rson);
    pushup(rt);
}

void Insert(int x,int l,int r,int rt){
    if(l==r){
        VL[rt]=rnk[l]%m*fib[mm+S[rt]]%m;
        V[rt]=rnk[l]%m*fib[mm+S[rt]+1]%m;
        return ;
    }
    int m=(l+r)>>1;
    pushdown(rt);
    if(x<=m){
        Insert(x,lson);
        S[rt<<1|1]++;
        shift(rt<<1|1,1);
    }
    else Insert(x,rson);
    pushup(rt);
}

void del(int x){
    mp[rnkA[x]]--;
    if(mp[rnkA[x]]==0)move(rnkA[x],1,mm,1);
}

void add(int x){
    mp[rnkA[x]]++;
    if(mp[rnkA[x]]==1)Insert(rnkA[x],1,mm,1);
}

void getfib(){
    fib[mm]=0;
    fib[mm+1]=1;
    for(int i=2;i<=mm;i++)fib[mm+i]=(fib[mm+i-1]+fib[mm+i-2])%m;
    for(int i=1;i<=mm;i++)fib[mm-i]=(fib[mm-i+2]-fib[mm-i+1]+m)%m;
}

int main(){
    scanf("%d%d",&n,&m);
    int sz=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        pos[i]=i/sz;
        rnk[i]=a[i];
    }
    init();
    getfib();
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id=i;
    }
    sort(Q+1,Q+1+q,cmp);
    for(int i=1;i<=q;i++){
        while(L>Q[i].l){
            L--;
            add(L);
        }
        while(L<Q[i].l){
            del(L);
            L++;
        }
       while(R<Q[i].r){
            R++;
            add(R);
        }
        while(R>Q[i].r){
            del(R);
            R--;
        }
        res[Q[i].id]=V[1];
    }
    for(int i=1;i<=q;i++){
        printf("%d\n",res[i]);
    }
    return 0;
}

 

posted @ 2018-04-25 19:04  采蘑菇的小西佬  阅读(318)  评论(0编辑  收藏  举报