【bzoj4553】[Tjoi2016&Heoi2016]序列【树套树 树状数组套平衡树】

洛谷传送门
大视野传送门
这道题刚开始看上去没有思路,不过慢慢分析有了。
我们设3个数组:
a[i] a [ i ] 表示原来第i个位置上的值。
maxn[i] m a x n [ i ] 表示第i个位置上可以变成的最大值。
minn[i] m i n n [ i ] 表示第i个位置上可以变成的最小值。
要满足在任意一种变化中,选出的子序列中第 i i 个位置的上一个位置j是符合要求的,需要满足:
1.j<i 1. j < i 这一条很显然。
2.maxn[j]<=a[i] 2. m a x n [ j ] <= a [ i ] 当j的位置上的数变成最大值时序列仍然不降。
3.a[j]<=minn[i] 3. a [ j ] <= m i n n [ i ] 当i的位置上的数变成最小值时序列仍然不降。
于是一个dp就很显然了:
f[i]=max(f[j]) f [ i ] = m a x ( f [ j ] ) ,j要满足上述条件。
可以发现,有1,2,3这3条要求,不就是一个三维偏序问题吗?跟陌上花开那道题非常像。
首先从小到大枚举i,可以降掉第一维。
第二维和第三维直接树套树搞定。
我写了树状数组套Treap,时间复杂度和空间复杂度都是比较稳的。
第一次1A树套树祭
代码:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=100005;
int n,m,k,x,y,ans=1,a[N],maxn[N],minn[N],f[N];
int cnt,ch[N*16][2],siz[N*16],val[N*16],key[N*16],mx[N*16],rnd[N*16];
struct Treap{
    int rt;
    void pushup(int k){
        siz[k]=siz[ch[k][0]]+siz[ch[k][1]]+1;
        mx[k]=max(max(mx[ch[k][0]],mx[ch[k][1]]),key[k]);
    }
    void rotate(int &y,int md){
        int x=ch[y][md];
        ch[y][md]=ch[x][!md];
        ch[x][!md]=y;
        pushup(y);
        pushup(x);
        y=x;
    }
    void insert(int &k,int v,int x){
        if(!k){
            k=++cnt;
            val[k]=v;
            key[k]=mx[k]=x;
            rnd[k]=rand();
            siz[k]=1;
            return;
        }
        siz[k]++;
        if(v==val[k]){
            key[k]=max(key[k],x);
        }else if(v<val[k]){
            insert(ch[k][0],v,x);
            if(rnd[ch[k][0]]>rnd[k]){
                rotate(k,0);
            }
        }else{
            insert(ch[k][1],v,x);
            if(rnd[ch[k][1]]>rnd[k]){
                rotate(k,1);
            }
        }
        pushup(k);
    }
    int query(int x){
        int k=rt,res=0;
        while(k){
            if(x==val[k]){
                res=max(res,key[k]);
                break;
            }else if(x<val[k]){
                k=ch[k][0];
            }else{
                res=max(res,max(key[k],mx[ch[k][0]]));
                k=ch[k][1];
            }
        }
        return res;
    }
}t[N];
inline int lowbit(int x){
    return x&(-x);
}
void add(int i,int j,int v){
    while(i<=k){
        t[i].insert(t[i].rt,j,v);
        i+=lowbit(i);
    }
}
int qmax(int i,int j){
    int res=0;
    while(i){
        res=max(res,t[i].query(j));
        i-=lowbit(i);
    }
    return res;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        maxn[i]=minn[i]=a[i];
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        maxn[x]=max(maxn[x],y);
        minn[x]=min(minn[x],y);
        k=max(k,maxn[x]);
    }
    f[1]=1;
    add(maxn[1],a[1],1);
    for(int i=2;i<=n;i++){
        f[i]=qmax(a[i],minn[i])+1;
        add(maxn[i],a[i],f[i]);
        ans=max(ans,f[i]);
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值