BZOJ4553 [Tjoi2016&Heoi2016]序列

设一个数的原值为v[i],最小能变化的值为l[i],最大值为r[i],观察一下可发现,i可放在j后面当且仅当r[j]<=v[i]且v[j]<=l[i],这样我们可以写出一个n^2的dp,dp中取max的过程相当于在左下角为(0,0),右上角为(v[i],l[i])的矩形内查询最大值,对f[i]赋值相当于在(r[i],v[i])加一个点,权值为f[i],这个过程可用树套树或KDT或CDQ分治实现

然而我写了一发树套树T了,今天模拟,考了一道题,也是dp最长某某某条件子序列转化为矩形最大值和加点,我又怒写一发树套树,结果又T了,xuruifan怒写KDT,T了,commonc怒写CDQ分治,过掉了-_-

然后当个双倍经验,我就写了这个CDQ分治

复杂度O(n log^2 n)

#include<iostream>  
#include<cstdlib>  
#include<cstdio>  
#include<cstring>  
#include<cmath>  
#include<ctime>  
#include<algorithm>  
#include<iomanip>  
#include<vector>  
#include<stack>  
#include<queue>  
#include<map>  
#include<set>  
#include<bitset>  
using namespace std;  
#define MAXN 300010  
#define MAXM 1010  
#define ll long long  
#define INF 1000000000  
#define MOD 1000000007  
#define eps 1e-8  
struct que{  
    int x;  
    int y;  
    int z;  
    int v;  
    bool cq;  
    friend bool operator <(que x,que y){  
        return x.x<y.x;  
    }  
};  
map<int,int>h;  
int tls[MAXN],tln,mx;  
int n,m;  
int l[MAXN],r[MAXN],v[MAXN];  
int T;  
int f[MAXN];  
int c[MAXN];  
int vis[MAXN];  
que a[MAXN],ta[MAXN];  
inline int lb(int x){  
    return x&-x;  
}  
void change(int x,int y){  
    for(;x<=mx;x+=lb(x)){  
        if(vis[x]!=T){  
            c[x]=0;  
        }  
        vis[x]=T;  
        c[x]=max(c[x],y);  
    }  
}  
int ask(int x){  
    int re=0;  
    for(;x;x-=lb(x)){  
        if(vis[x]!=T){  
            c[x]=0;  
        }  
        vis[x]=T;  
        re=max(re,c[x]);  
    }  
    return re;  
}  
void cdq(int l,int r){  
      
    if(l==r){  
        if(!a[l].cq){  
            f[a[l].v]++;
        }  
        return ;  
    }  
    int i,j,mid=l+r>>1,l1=l,l2=mid+1;  
    for(i=l;i<=r;i++){  
        if(a[i].z<=mid){  
            ta[l1++]=a[i];  
        }else{  
            ta[l2++]=a[i];  
        }  
    }  
    memcpy(a+l,ta+l,sizeof(que)*(r-l+1));  
    cdq(l,mid);  
    T++;  
    j=l;  
    for(i=mid+1;i<=r;i++){  
        for(;j<=mid&&a[j].x<=a[i].x;j++){  
            if(a[j].cq){  
                change(a[j].y,f[a[j].v]);  
            }  
        }  
        if(!a[i].cq){  
            f[a[i].v]=max(f[a[i].v],ask(a[i].y));  
        }  
    }  
    cdq(mid+1,r);  
    l1=l,l2=mid+1;  
    for(i=l;i<=r;i++){  
        if(a[l1]<a[l2]&&l1<=mid||l2>r){  
            ta[i]=a[l1++];  
        }else{  
            ta[i]=a[l2++];  
        }  
    }  
    memcpy(a+l,ta+l,sizeof(que)*(r-l+1));  
}  
int main(){  
    int i,x,y;  
    int tmp=0;  
    scanf("%d%d",&n,&m);  
    for(i=1;i<=n;i++){  
        scanf("%d",&v[i]);  
        l[i]=r[i]=v[i];  
    }  
    for(i=1;i<=m;i++){  
        scanf("%d%d",&x,&y);  
        l[x]=min(l[x],y);  
        r[x]=max(r[x],y);  
    }  
    for(i=1;i<=n;i++){  
        tls[++tln]=v[i];  
        tls[++tln]=l[i];  
        tls[++tln]=r[i];  
    }  
    sort(tls+1,tls+tln+1);  
    for(i=1;i<=tln;i++){  
        if(tls[i]!=tls[i-1]){  
            h[tls[i]]=++mx;  
        }  
    }  
    for(i=1;i<=n;i++){  
        v[i]=h[v[i]];  
        l[i]=h[l[i]];  
        r[i]=h[r[i]];  
    }  
    int tim=0;  
    for(i=1;i<=n;i++){  
        a[++tim].z=tim;  
        a[tim].x=v[i];  
        a[tim].y=l[i];  
        a[tim].cq=0;  
        a[tim].v=i;  
        a[++tim].z=tim;  
        a[tim].x=r[i];  
        a[tim].y=v[i];  
        a[tim].cq=1;  
        a[tim].v=i;  
    }  
    sort(a+1,a+tim+1);  
    cdq(1,tim);  
    int ans=0;  
    for(i=1;i<=n;i++){  
        ans=max(ans,f[i]);  
    }  
    printf("%d\n",ans);  
    return 0;  
}  
  
/* 
2 2  
7 10
1 5   
1 9  
 
*/  


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值