[HEOI2016] 序列

Description

有n个数,每个数有若干取值,但是只能在原数列的一个位置变换取值,求一个最长上升子序列,满足无论数列如何变化,这都是一个最长上升子序列。

Solution

记录 \(l[i],r[i]\) 分别表示 \(i\) 能取到的最大最小值,\(val[i]\) 为原数列。

我们来看看满足条件的二元组 \(i,j\) 满足什么条件。

  1. \(i<j\)
  2. \(val[i]<val[j]\)
  3. \(r[i]<val[j]\)
  4. \(val[i]<l[j]\)

观察到条件2包含在条件3,4里。

二维偏序问题,上CDQ就行。

Code

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define N 100005
#define min(A,B) ((A)<(B)?(A):(B))
#define max(A,B) ((A)>(B)?(A):(B))
#define swap(A,B) ((A)^=(B)^=(A)^=(B))

int f[N];
int ans[N];
int n,m,len;
int last[N];

struct Node{
    int val,l,r,idx;
}node[N];

bool cmp(Node x,Node y){
    return x.l<y.l;
}

bool cmp2(Node x,Node y){
    return x.idx<y.idx;
}

bool cmp3(Node x,Node y){
    return x.val<y.val;
}

void add(int x,int y){
    for(;x<=len;x+=x&-x)
        f[x]=max(f[x],y);
}

int query(int x){
    int now=0;
    for(;x;x-=x&-x)
        now=max(now,f[x]);
    return now;
}

int getint(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch)) f|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

void cdq(int l,int r){
    if(l>=r) return;
    int mid=l+r>>1;
    cdq(l,mid);
    std::sort(node+l,node+mid+1,cmp3);
    std::sort(node+mid+1,node+r+1,cmp);
    int a=l; memset(f,0,sizeof f);
    for(int j=mid+1;j<=r;j++){
        while(a<=mid and node[a].val<=node[j].l){
            add(node[a].r,ans[node[a].idx]);
            a++;
        }
        int p=query(node[j].val);
        ans[node[j].idx]=max(ans[node[j].idx],p+1);
    }
    std::sort(node+l,node+r+1,cmp2);
    cdq(mid+1,r);
}

signed main(){
    n=getint(),m=getint();
    for(int i=1;i<=n;i++){
        ans[i]=1;
        node[i].val=node[i].l=node[i].r=getint();
        len=max(len,node[i].l);
        node[i].idx=i;
    }
    for(int i=1;i<=m;i++){
        int x=getint(),y=getint();
        len=max(len,y);
        node[x].r=max(node[x].r,y);
        node[x].l=min(node[x].l,y);
    }
    cdq(1,n);
    int maxn=0;
    for(int i=1;i<=n;i++)
        maxn=max(maxn,ans[i]);
    printf("%d\n",maxn);
    return 0;
}

转载于:https://www.cnblogs.com/YoungNeal/p/9374452.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值