【bzoj4553】【Tjoi2016】【Heoi2016】【序列】【树套树】【线段树套线段树】

来源:互联网 发布:淘宝发布宝贝没有规格 编辑:程序博客网 时间:2024/06/07 14:40

题目大意

给出长度为n的序列a,有m次变化。每次变化把a[x]改成b[x],一个位可以多次修改,变化相对独立。选出最长字串,在m次变化与原串中都是不下降的。

题解

通过分析可知,设f[i]为前i位中一定选了i所形成的最长不下降字串的长度,f[i]可以转移到f[j] (j>i)当且仅当满足三个条件。a[i]<=a[j]。max(b[i])<=a[j]。a[i]<=min(b[j])。把a[i]也当作b[i],则要满足max(b[i])<=a[j]。a[i]<=min(b[j])。我们可以O(n^2)地dp。

其实我们只用满足两个关键字都小于或等于当前两个关键字就可以转移,我们考虑线段树套线段树。外层线段树代表第一关键字,内层代表第二关键字,把f值放进去,单点修改,区间查询就可以了。

详细点就是外层的每个区间[l,r]都拥有一颗关于第二关键字的线段树,每个包含第一关键字的[l,r]都需要按第二关键字在自己的线段树上插入f值。再详细就是看代码了。

其实这题可以用cdq分治,或k-d tree,或其他树套树来做。

code

#include<set>#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,j,k) for(int i=j;i<=k;i++)#define fd(i,j,k) for(int i=j;i>=k;i--)using namespace std;int const maxn=100000;int n,m,pon=1,a[maxn+10],mx[maxn+10],mi[maxn+10],son[maxn*108+10][3];void change2(int now,int l,int r,int mxi,int fi){    int m=(l+r)/2;    son[now][2]=max(son[now][2],fi);    if(l==r)return;    else if(mxi<=m){        if(!son[now][0])son[now][0]=++pon;        change2(son[now][0],l,m,mxi,fi);    }else{        if(!son[now][1])son[now][1]=++pon;        change2(son[now][1],m+1,r,mxi,fi);    }}void change(int now,int l,int r,int ai,int mxi,int fi){    int m=(l+r)/2;    if(!son[now][2])son[now][2]=++pon;    change2(son[now][2],1,maxn+1,mxi,fi);    if(l==r)return;    else if(ai<=m){        if(!son[now][0])son[now][0]=++pon;        change(son[now][0],l,m,ai,mxi,fi);    }else{        if(!son[now][1])son[now][1]=++pon;        change(son[now][1],m+1,r,ai,mxi,fi);    }}int get2(int now,int l,int r,int a0,int ai){    int m=(l+r)/2;    if((l==a0)&&(r==ai))return son[now][2];    else if(ai<=m){        if(!son[now][0])return 0;        return get2(son[now][0],l,m,a0,ai);    }else{        int tmp=0;        if(son[now][1])tmp=get2(son[now][1],m+1,r,m+1,ai);        if(son[now][0])tmp=max(tmp,son[son[now][0]][2]);        return tmp;    }}int get(int now,int l,int r,int mi0,int mii,int a0,int ai){    int m=(l+r)/2;    if((l==mi0)&&(r==mii)){        if(!son[now][2])return 0;        return get2(son[now][2],1,maxn+1,a0,ai);    }    else if(mii<=m){        if(!son[now][0])return 0;        return get(son[now][0],l,m,mi0,mii,a0,ai);    }else{        int tmp=0;        if(son[now][1])tmp=get(son[now][1],m+1,r,m+1,mii,a0,ai);        if(son[now][0]&&son[son[now][0]][2])tmp=max(tmp,get2(son[son[now][0]][2],1,maxn+1,a0,ai));        return tmp;    }}int main(){    //freopen("len.in","r",stdin);    //freopen("len.out","w",stdout);    freopen("d.in","r",stdin);    freopen("d.out","w",stdout);    scanf("%d%d",&n,&m);    fo(i,1,n)scanf("%d",&a[i]),a[i]++,mx[i]=mi[i]=a[i];    fo(i,1,m){        int x,y;scanf("%d%d",&x,&y);y++;        mx[x]=max(mx[x],y);        mi[x]=min(mi[x],y);    }    int ans=1;    change(1,1,maxn+1,1,1,0);    fo(i,1,n){        int f;        ans=max(ans,f=get(1,1,maxn+1,1,mi[i],1,a[i])+1);        change(1,1,maxn+1,a[i],mx[i],f);    }    printf("%d",ans);    return 0;}
0 0