[TJOI&HEOI2016]seq/[JZOJ4606]序列

本文介绍了一种利用主席树和动态规划求解最长不降子序列的问题,通过处理序列变换来寻找最佳子序列,采用树状数组套线段树的方法,并提出CDQ分治作为优化方案。

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

题目大意

一个长度为n的序列a,有m种变换可能,将ax变为y
现在你需要求出一个子序列,使得不管发生哪种变换(最多只会发生一种变换,可能不发生),该序列都是不下降的。
1n,m,ai,y105


题目分析

定义mxi为位置i上的数最大变化值,mii为最小变化值。
动态规划

fi=maxj<i,mxjai,ajmii{fj}+1

第一个约束顺序解决。
第二三个约束看成二维平面上的点,树套树解决,空间要卡好,不要炸了。我码了棵树状数组套线段树,时间复杂度O(nlog22n),空间复杂度远小于上界O(nlog22n)
然而第二个约束可以使用cdq分治解决,虽然时间复杂度也是O(nlog22n),但常数较树套树少很多,而且空间复杂度O(n)

代码实现

#include <iostream>
#include <cstdio>
#include <cctype>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int S=10050000;
const int N=100005;
const int A=100000;
const int LGA=17;

int tmp[LGA];

int lowbit(int x){return x&-x;}

struct chairman_tree
{
    int son[S][2],v[S];
    int root[A+10];
    int tot;

    int newnode(){return ++tot;}

    int query(int *rt,int st,int en,int l,int r)
    {
        int i,ret=0;
        if (st==l&&en==r)
        {
            for (i=1;i<=rt[0];i++) ret=max(v[rt[i]],ret);
            return ret;
        }
        int mid=l+r>>1,t[LGA];
        t[0]=rt[0];
        if (en<=mid)
        {
            for (i=1;i<=rt[0];i++) t[i]=son[rt[i]][0];
            return query(t,st,en,l,mid);
        }
        else
            if (mid+1<=st)
            {
                for (i=1;i<=rt[0];i++) t[i]=son[rt[i]][1];
                return query(t,st,en,mid+1,r);
            }
            else
            {
                for (i=1;i<=rt[0];i++) t[i]=son[rt[i]][0];
                ret=query(t,st,mid,l,mid);
                for (i=1;i<=rt[0];i++) t[i]=son[rt[i]][1];
                ret=max(query(t,mid+1,en,mid+1,r),ret);
                return ret;
            }
    }

    void modify(int *rt,int x,int l,int r,int edit)
    {
        int i,ret=0;
        for (i=1;i<=rt[0];i++)
        {
            if (!rt[i]) rt[i]=newnode();
            v[rt[i]]=max(v[rt[i]],edit);
        }
        if (l==r) return;
        int mid=l+r>>1,t[LGA];
        t[0]=rt[0];
        if (x<=mid)
        {
            for (i=1;i<=rt[0];i++) t[i]=son[rt[i]][0];
            modify(t,x,l,mid,edit);
            for (i=1;i<=rt[0];i++) son[rt[i]][0]=t[i];
        }
        else
        {
            for (i=1;i<=rt[0];i++) t[i]=son[rt[i]][1];
            modify(t,x,mid+1,r,edit);
            for (i=1;i<=rt[0];i++) son[rt[i]][1]=t[i];
        }
    }
}t;

int a[N],mi[N],mx[N],f[N];
int n,m,ans;

void dp()
{
    for (int i=1,j,l;i<=n;i++)
    {
        for (l=mi[i],tmp[0]=0;l;l-=lowbit(l)) tmp[++tmp[0]]=t.root[l];
        f[i]=t.query(tmp,1,a[i],1,A)+1;
        ans=max(ans,f[i]);
        for (l=a[i],tmp[0]=0;l<=A;l+=lowbit(l)) tmp[++tmp[0]]=t.root[l];
        t.modify(tmp,mx[i],1,A,f[i]);
        for (l=a[i],j=1;l<=A;j++,l+=lowbit(l)) t.root[l]=tmp[j];
    }
}

int main()
{

    freopen("sequence.in","r",stdin),freopen("sequence.out","w",stdout);
    n=read(),m=read();
    for (int i=1;i<=n;i++) mi[i]=mx[i]=a[i]=read();
    for (int i=1,x,y;i<=m;i++)
    {
        x=read(),y=read();
        mi[x]=min(mi[x],y),mx[x]=max(mx[x],y);
    }
    dp();
    printf("%d\n",ans);
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值