BZOJ4553: [Tjoi2016&Heoi2016]序列

输入

佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值

可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你
,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可
。注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:
1 2 3
2 2 3
1 3 3
1 1 31 2 4
选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:3 3 33 2 3选择子序列
为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求


输出

输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的

状态。接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。1 <= x <= n。所有数字均为正整数
,且小于等于100,000


Solution

首先想到n^2的DP,和n^2的LIS是一个道理

for(int i=1;i<=n;++i){
        for(int j=1;j<i;++j){
            if(maxv[j]<=a[i]&&a[j]<=minv[i]) dp[i]=max(dp[i],dp[j]+1);
        }
        ans=max(ans,dp[i]);
}

minv,maxv分别表示a[i]能变化的最小值和最大值
要求的满足这两个不等式的最大的dp值
其实就是一个二位数点问题,二维线段树就可以做
洛谷上轻松AC,BZOJ被MLE
然后改空间,手动二分数组大小
这里写图片描述
最后发现数组大小*108是AC的

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson o<<1,l,Mid
#define rson o<<1|1,Mid+1,r
#define MID Mid=(l+r)>>1
#define maxn 100010
int mx;
inline int read(){
    int ret=0,ff=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') ff=-ff;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        ret=ret*10+ch-'0';
        ch=getchar();
    }
    return ret*ff;
}
int maxv[maxn],minv[maxn],dp[maxn],a[maxn];
int siz=0;
int tr[maxn*108];
int ls[maxn*108],rs[maxn*108],rt[maxn<<2];
void insertY(int &o,int l,int r,int x,int val){
    if(o==0) o=++siz;
    if(l==r) tr[o]=max(tr[o],val);
    else{
        int MID;
        if(x<=Mid) insertY(ls[o],l,Mid,x,val);
        else insertY(rs[o],Mid+1,r,x,val);
        tr[o]=max(tr[ls[o]],tr[rs[o]]);
    }
    return ;
}
void insertX(int o,int l,int r,int qx,int qy,int val){
    insertY(rt[o],1,mx,qy,val);
    if(l==r) return ;
    int MID;
    if(qx<=Mid) insertX(lson,qx,qy,val);
    else insertX(rson,qx,qy,val);
}
int queryY(int o,int l,int r,int ql,int qr){
    if(o==0) return 0;
    if(ql<=l&&r<=qr) return tr[o];
    int MID,ret=0;
    if(ql<=Mid) ret=queryY(ls[o],l,Mid,ql,qr);
    if(qr>Mid) ret=max(ret,queryY(rs[o],Mid+1,r,ql,qr));
    return ret;
}
int queryX(int o,int l,int r,int xl,int xr,int yl,int yr){
    if(xl<=l&&r<=xr) return queryY(rt[o],1,mx,yl,yr);
    int MID,ret=0;
    if(xl<=Mid) ret=queryX(lson,xl,xr,yl,yr);
    if(xr>Mid) ret=max(ret,queryX(rson,xl,xr,yl,yr));
    return ret;
}
int main(){
//  freopen("bzoj4553.in","r",stdin);
    int n=read(),m=read(),ans=0;
    for(int i=1;i<=n;++i) maxv[i]=minv[i]=a[i]=read();
    while(m--){
        int x=read(),y=read();
        maxv[x]=max(maxv[x],y);
        mx=max(mx,maxv[x]);
        minv[x]=min(minv[x],y);
    }
    for(int i=1;i<=n;++i){
        dp[i]=queryX(1,1,mx,1,a[i],1,minv[i])+1;
        insertX(1,1,mx,maxv[i],a[i],dp[i]);
        ans=max(ans,dp[i]);
    }
    printf("%d\n",ans);
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值