zoukankan      html  css  js  c++  java
  • 「CEOI2018」斐波那契表示法

    「CEOI2018」斐波那契表示法

    思路:维护当前数值的唯一表示法,然后根据唯一表示法来确定答案

    Part1 唯一表示法

    任何一个数(x)有唯一表示(P_i),满足(x=sum F_{P_i},P_i<P_{i+1}-1)

    即不会出现相邻两项

    依次插入每一个数(x),考虑可能出现的情况

    1.(x)一位以及前后为空,那么直接插入

    2.(x)一位为空,且(x-1)为空,(x+1)已经出现

    删除(x+1),插入(x+2)

    3.(x)一位为空,且(x+1)为空,(x-1)已经出现

    删除(x-1),插入(x+1)

    4.(x)一位有

    先删除(x),然后插入(x+1,x-2)

    对于操作1,2,3以及4中的(x+1),每次操作增加(O(1))个元素,每次递归进行删除(O(1))个元素

    操作次数为均摊(O(n))

    对于(4)操作中的(x-2),如果(x-2)已经出现就会不断进行递归

    最终的效果就是所有被操作到的(x-2,x-4,x-6ldots)向右平移了一位

    大致如此,实际情况比较复杂,要讨论x-2=0,x-2<0等等情况

    用一棵平衡树维护(P_i-P_{i-1})的值即可,4操作可以二分左边第一个>2的元素,然后进行平移

    最终复杂度为(O(nlog n))

    [ ]

    Part2 dp求答案

    令边界(P_0=0),根据上面维护的(delta_i=P_i-P_{i-1})

    考虑根据(delta_i)求解答案

    显然一个数(x)可以下分为(x)或者(x-1,x-2)(x-1,x-3,x-4)(x-1,x-3,x-5,x-6ldots)

    且不能碰到前面的数

    简单分析发现(P_i)有$lceil frac{delta_i}{2} ceil $种下分方案

    然而,(P_{i-1})如果被下分,那么(P_{i-1})这一位会消失,变成(P_{i-1}-1)作为限制点

    也就是说,(P_{i-1})的下分会影响到(delta_i),使得(delta_i ightarrow delta_i+1)

    那么依次考虑每个(delta_i),令(dp_{i,f})表示前(i)个,最后一个是否下分的方案数,可以(dp)求解

    由于要动态维护,因此可以考虑用一个类似矩阵的东西来维护区间的dp情况

    在平衡树中(up)维护答案即可

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair <int,int> Pii;
    #define reg register
    #define mp make_pair
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
    template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
    
    char IO;
    template <class T=int> T rd(){
        T s=0; int f=0;
        while(!isdigit(IO=getchar())) f|=IO=='-';
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return f?-s:s;
    }
    
    const int N=4e5+10,P=1e9+7;
    
    int n;
    struct Node{
        int l,r,ma,len;
        int a[2][2];
        void clear(){ memset(a,0,sizeof a); }
        Node(){ } 
        Node(int x){
            l=r=ma=len=x;
            rep(i,0,1) {
                a[i][0]=1;
                a[i][1]=(x-1+i)/2;
            }
        }
        Node operator + (const Node _) const {
            Node res; res.l=l,res.r=_.r,res.ma=max(ma,_.ma),res.len=len+_.len;
            res.a[0][0]=(1ll*a[0][0]*_.a[0][0]+1ll*a[0][1]*_.a[1][0])%P;
            res.a[1][0]=(1ll*a[1][0]*_.a[0][0]+1ll*a[1][1]*_.a[1][0])%P;
            res.a[0][1]=(1ll*a[0][0]*_.a[0][1]+1ll*a[0][1]*_.a[1][1])%P;
            res.a[1][1]=(1ll*a[1][0]*_.a[0][1]+1ll*a[1][1]*_.a[1][1])%P;
            return res;
        }
    } s[N],val[N];
    
    int rt,ls[N],rs[N],key[N];
    void Up(int x){
        s[x]=val[x];
        if(ls[x]) s[x]=s[ls[x]]+s[x];
        if(rs[x]) s[x]=s[x]+s[rs[x]];
    }
    int U(int x,int y){
        if(!x||!y) return x|y;
        if(key[x]<key[y]) return rs[x]=U(rs[x],y),Up(x),x;
        return ls[y]=U(x,ls[y]),Up(y),y;
    }
    
    Pii Lower(int x,int len){
        if(len<=0 || !x) return mp(0,x);
        if(s[x].len<=len) return mp(x,0);
        if(s[ls[x]].len>=len) {
            Pii y=Lower(ls[x],len);
            return ls[x]=y.second,Up(x),mp(y.first,x);
        } else {
            Pii y=Lower(rs[x],len-s[ls[x]].len-val[x].len);
            return rs[x]=y.first,Up(x),mp(x,y.second);
        }
    }
    
    void EraseEnd(int &x){
        if(!rs[x]){ x=ls[x]; return; }
        static int T[N],C;
        for(int y=x;y;y=rs[y]) T[++C]=y;
        rs[T[C-1]]=ls[T[C]];
        drep(i,C-1,1) Up(T[i]);
        C=0;
    }
    void AddR(int x,int y){
        if(!x) return;
        if(rs[x]) return AddR(rs[x],y),Up(x);
        val[x]=Node(val[x].len+y),Up(x);
    }
    void AddL(int x,int y){
        if(!x) return;
        if(ls[x]) return AddL(ls[x],y),Up(x);
        val[x]=Node(val[x].len+y),Up(x);
    }
    
    Pii Split(int x){
        if(s[x].ma<=2) return mp(0,x);
        if(val[x].ma<=2 && s[rs[x]].ma<=2) {
            Pii y=Split(ls[x]);
            return ls[x]=y.second,Up(x),mp(y.first,x);
        } else {
            Pii y=Split(rs[x]);
            return rs[x]=y.first,Up(x),mp(x,y.second);
        }
    }
    Pii Split2(int x){
        if(s[x].ma<=2) return mp(x,0);
        if(s[ls[x]].ma>2 || val[x].ma>2) {
            Pii y=Split2(ls[x]);
            return ls[x]=y.second,Up(x),mp(y.first,x);
        } else {
            Pii y=Split2(rs[x]);
            return rs[x]=y.first,Up(x),mp(x,y.second);
        }
    }
    int New(int x){ return key[++n]=rand(),s[n]=val[n]=Node(x),n; }
    
    void Ins(int x){
        if(x<0) return;
        cmax(x,1);
        if(!rt) { rt=New(x); return; }
        if(s[rt].len<x-1) {
            rt=U(rt,New(x-s[rt].len));
            return;
        }
        if(s[rt].len==x-1) {
            EraseEnd(rt);
            return Ins(x+1);
        }
        Pii t=Lower(rt,x);
        if(s[t.first].len!=x) {
            if(x>1) {
                Pii y=Lower(t.first,x-1);
                if(s[y.first].len==x-1) {
                    AddR(y.first,s[y.second].len),rt=U(y.first,t.second);
                    return Ins(x+1);
                } 
                t.first=U(y.first,y.second);
            }
            if(s[t.first].len==x+1) {
                Pii y=Split2(t.second);
                AddL(y.second,-1);
                int d=s[t.first].r+s[y.first].len+1;
                EraseEnd(t.first);
                rt=U(U(t.first,New(d)),y.second);
                return;
            }
            int d=s[t.first].len-x;
            AddR(t.first,-d);
            rt=U(U(t.first,New(d)),t.second);
            return;
        }
        if(s[t.second].l==2) return AddL(t.second,s[t.first].r),EraseEnd(t.first),rt=U(t.first,t.second),Ins(x+1),Ins(x-2);
        Pii y=Split(t.first); AddL(t.second,-1);
        if(!y.first) {
            if(s[y.second].l==1) {
                AddL(y.second,1),rt=U(y.second,t.second);
                return;
            }
            rt=U(U(New(1),y.second),t.second);
            return;
        } 
        if(s[y.first].len>3 && s[y.first].r==3) {
            EraseEnd(y.first),AddR(y.first,2);
            rt=U(U(U(y.first,New(2)),y.second),t.second);
            return;
        }
        AddR(y.first,-2);
        rt=U(U(U(y.first,New(3)),y.second),t.second);
    }
    
    
    int main(){
        rep(kase,1,rd()) Ins(rd()),printf("%d
    ",(s[rt].a[0][0]+s[rt].a[0][1])%P);
    }
    
    
  • 相关阅读:
    九.Protobuf3特殊类型
    八.Protobuf3更新消息类型(添加新的字段)
    七.Protobuf3 嵌套类型
    六.Protobuf3引入其他.proto文件
    五.Protobuf3 枚举
    四.Protobuf3 缺省值
    VC 在调用main函数之前的操作
    Windows下的代码注入
    C 堆内存管理
    VC++ 崩溃处理以及打印调用堆栈
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14452183.html
Copyright © 2011-2022 走看看