[SCOI2014]方伯伯的OJ

本文介绍了一种使用两棵平衡树解决特定问题的方法。通过建立编号和排名两棵平衡树,利用pair记录节点信息,文章详细展示了如何处理各种操作,并提供了一个具体的代码实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述
数据范围n<=108,m<=105n<=108,m<=105
一眼平衡树。5分钟出思路,调了3小时。
我们不好直接维护排名的平衡树,也不好直接维护编号的平衡树。
那就建22颗平衡树,一棵以编号中序遍历,一棵以排名中序遍历,每个节点开pair记录下这排名和编号。
然后对于每个操作 直接按照题意去修改就好了。把连续的区间缩成点用的时候再拆开。
注意以编号中序遍历中 pairpair 的排名并不是真实排名而是相对排名,需要到另一棵树中再查找。
findszfindsz 操作中如果要找的点被那段区间包含了 就直接强行 splitsplit 拆开返回szsz

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e6+5;

typedef pair<int,int>par;
#define mp make_pair

struct treap{
    par val[MAXN];
    int prio[MAXN],size[MAXN],lson[MAXN],rson[MAXN],lm[MAXN],rm[MAXN],rt,cnt;
    inline int get(int p){
        return rm[p]-lm[p]+1;
    }
    inline void pushup(int p){
        size[p]=size[lson[p]]+size[rson[p]]+get(p);
    }
    inline int newnode(int l,int r){
        ++cnt;
        if(l==r)val[cnt].first=l,val[cnt].second=l;
        prio[cnt]=rand();size[cnt]=1;
        lson[cnt]=rson[cnt]=0;lm[cnt]=l,rm[cnt]=r;
        return cnt;
    }
    par split(int p,int x){
        if(!x)return mp(0,p);
        int l=lson[p],r=rson[p];
        if(x<=size[l]){
            par tem=split(l,x);
            lson[p]=tem.second;pushup(p);return mp(tem.first,p);
        }
        else if(x>=size[l]+get(p)){
            par tem=split(r,x-size[l]-get(p));
            rson[p]=tem.first;pushup(p);return mp(p,tem.second);    
        }
        x-=size[l];
        int tem=rm[p];rm[p]=lm[p]+x-1;
        if(rm[p]==lm[p])val[p]=mp(lm[p],lm[p]);
        int pp=newnode(rm[p]+1,tem);
        pp=merge(pp,r);rson[p]=0;
        pushup(p);pushup(pp);
        return mp(p,pp);
    }
    int merge(int x,int y){
        if(!x){pushup(y);return y;}
        if(!y){pushup(x);return x;}
        if(prio[x]<prio[y]){
            rson[x]=merge(rson[x],y);pushup(x);return x;
        }
        lson[y]=merge(x,lson[y]);pushup(y);return y;
    }
    int Split(int p,int v,int posp){
        int l=lm[p],r=rm[p],sz=v-l;
        par t1=split(rt,posp+sz);
        par t2=split(t1.second,1);
        rt=merge(t1.first,merge(t2.first,t2.second));
        return posp+sz;
    }
    int findsz(int x){
        int p=rt,sz=0;
        while(p){
            if(rm[p]>=x&&lm[p]<=x&&rm[p]-lm[p])
                return size[lson[p]]+Split(p,x,sz); 
            if(rm[p]-lm[p]==0){//对于单个点 
                if(val[p].first==x)return size[lson[p]]+sz;
                if(val[p].first>x)p=lson[p];
                else sz+=size[lson[p]]+get(p),p=rson[p];    
            }
            else {//区间 
                if(rm[p]>x)p=lson[p];
                else sz+=size[lson[p]]+get(p),p=rson[p];
            }
        }
        return sz;
    }
    void debug(int p){
        if(lson[p])debug(lson[p]);
        printf("%d ",val[p].second);
        if(rson[p])debug(rson[p]);
    }
}T[2];

int n,m,minn=0,maxx;

int change(int x,int y){
    int pos=T[0].findsz(x);
    par t1=T[0].split(T[0].rt,pos);
    par t2=T[0].split(t1.second,1);
    T[0].val[t2.first].first=y;
    int ret=T[0].val[t2.first].second;
    int put=T[1].findsz(ret)+1;
    printf("%d\n",put);
    T[0].rt=T[0].merge(t1.first,t2.second);
    int pos2=T[0].findsz(y);
    t1=T[0].split(T[0].rt,pos2);
    T[0].rt=T[0].merge(t1.first,T[0].merge(t2.first,t1.second));

    t1=T[1].split(T[1].rt,put-1);
    t2=T[1].split(t1.second,1);
    T[1].val[t2.first].second=y;
    T[1].rt=T[1].merge(t1.first,T[1].merge(t2.first,t2.second));
    return put;
}

int up(int x){
    int pos=T[0].findsz(x);
    par t1=T[0].split(T[0].rt,pos);
    par t2=T[0].split(t1.second,1);
    int ret=T[0].val[t2.first].second;
    int put=T[1].findsz(ret)+1;
    printf("%d\n",put);
    T[0].val[t2.first].second=--minn;
    T[0].rt=T[0].merge(t1.first,T[0].merge(t2.first,t2.second));

    t1=T[1].split(T[1].rt,put-1);
    t2=T[1].split(t1.second,1);
    T[1].val[t2.first].first=minn;
    T[1].rt=T[1].merge(t2.first,T[1].merge(t1.first,t2.second));
    return put;
}

int down(int x){
    int pos=T[0].findsz(x);
    par t1=T[0].split(T[0].rt,pos);
    par t2=T[0].split(t1.second,1);
    int ret=T[0].val[t2.first].second;
    int put=T[1].findsz(ret)+1;
    printf("%d\n",put);
    T[0].val[t2.first].second=++maxx;
    T[0].rt=T[0].merge(t1.first,T[0].merge(t2.first,t2.second));

    t1=T[1].split(T[1].rt,put-1);
    t2=T[1].split(t1.second,1);
    T[1].val[t2.first].first=maxx;
    T[1].rt=T[1].merge(t1.first,T[1].merge(t2.second,t2.first));
    return put;
}

int query(int x){
    int p=T[1].rt,ans=0;
    while(p){
        if(ans+T[1].size[T[1].lson[p]]+T[1].get(p)<x){
            ans+=T[1].size[T[1].lson[p]]+T[1].get(p);
            p=T[1].rson[p];
        }
        else if(ans+T[1].size[T[1].lson[p]]>=x)
            p=T[1].lson[p];
        else {
            if(T[1].rm[p]==T[1].lm[p]){
                printf("%d\n",T[1].val[p].second);
                return T[1].val[p].second;
            }
            else {
                x-=ans+T[1].size[T[1].lson[p]];
                printf("%d\n",T[1].lm[p]+x-1);
                return T[1].lm[p]+x-1;  
            }
        }
    }
}

int main(){
    int x,y;
    scanf("%d%d",&n,&m);
    minn=0,maxx=n+1;
    T[0].rt=T[0].newnode(1,n);T[1].rt=T[1].newnode(1,n);
    int pre=0;
    for(int i=1;i<=m;i++){
        int opt;
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d",&x,&y);
            pre=change(x-pre,y-pre);
        //  T[1].debug(T[1].rt);puts("one");
        }
        else if(opt==2){
            scanf("%d",&x);
        //  T[1].debug(T[1].rt);puts("s");
            pre=up(x-pre);  
        }
        else if(opt==3){
            scanf("%d",&x);
            pre=down(x-pre);
        //  T[1].debug(T[1].rt);puts("the");    
        //  puts("vvv");T[0].debug(T[0].rt);    
        //  puts("ggg");
        }
        else {
            scanf("%d",&x);
            pre=query(x-pre);
        }
    }
    return 0;
}
/*
10 10
1 2 11
3 13
2 5
3 7
2 8
2 10
2 11
3 14
2 18
4 9
*/
### 关于 SCOI2009 WINDY 数的解法 #### 定义与问描述 WINDY数是指对于任意两个相邻位置上的数字,它们之间的差至少为\(2\)。给定正整数区间\([L, R]\),计算该范围内有多少个WINDY数。 #### 动态规划法解析 为了高效解决这个问,可以采用动态规划的法来处理。定义状态`dp[i][j]`表示长度为`i`且最高位是`j`的WINDY数的数量[^3]。 - **初始化** 对于单个数字的情况(即只有一位),显然每一位都可以单独构成一个合法的WINDY数,因此有: ```cpp dp[1][d] = 1; // d ∈ {0, 1,...,9} ``` - **状态转移程** 当考虑多位数时,如果当前位选择了某个特定数值,则下一位的选择会受到限制——它必须满足与前一位相差不小于2的要求。具体来说就是当上一高位为`pre`时,当前位置可选范围取决于`pre`的具体取值: - 如果`pre >= 2`, 则可以选择`{0... pre-2}` - 否则只能从剩余的有效集合中选取 这样就可以通过遍历所有可能的状态来进行状态间的转换并累加结果。 - **边界条件处理** 特殊情况下需要注意的是,在实际应用过程中还需要考虑到给出区间的上下限约束。可以通过逐位比较的式判断是否越界,并据此调整有效状态空间大小。 ```cpp // 计算不超过num的最大windy数数量 int calc(int num){ int f[15], g[15]; memset(f, 0, sizeof(f)); string s = to_string(num); n = s.size(); for (char c : s) { a[++len] = c - '0'; } // 初始化f数组 for (int i=0;i<=9;++i)f[1][i]=1; // DP过程省略... return sum; } long long solve(long long L,long long R){ return calc(R)-calc(L-1); } ``` 此代码片段展示了如何利用预处理好的`dp`表快速查询指定范围内的WINDY数总量。其中`solve()`函数用于返回闭区间\[L,R\]内符合条件的总数;而辅助函数`calc()`负责根据传入参数构建相应的状态序列并最终得出答案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值