【bzoj4553】[Tjoi2016&Heoi2016]序列【树套树 树状数组套平衡树】
洛谷传送门
大视野传送门
这道题刚开始看上去没有思路,不过慢慢分析有了。
我们设3个数组:
表示原来第i个位置上的值。
表示第i个位置上可以变成的最大值。
表示第i个位置上可以变成的最小值。
要满足在任意一种变化中,选出的子序列中第个位置的上一个位置是符合要求的,需要满足:
这一条很显然。
当j的位置上的数变成最大值时序列仍然不降。
当i的位置上的数变成最小值时序列仍然不降。
于是一个dp就很显然了:
,j要满足上述条件。
可以发现,有1,2,3这3条要求,不就是一个三维偏序问题吗?跟陌上花开那道题非常像。
首先从小到大枚举i,可以降掉第一维。
第二维和第三维直接树套树搞定。
我写了树状数组套Treap,时间复杂度和空间复杂度都是比较稳的。
第一次1A树套树祭
代码:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=100005;
int n,m,k,x,y,ans=1,a[N],maxn[N],minn[N],f[N];
int cnt,ch[N*16][2],siz[N*16],val[N*16],key[N*16],mx[N*16],rnd[N*16];
struct Treap{
int rt;
void pushup(int k){
siz[k]=siz[ch[k][0]]+siz[ch[k][1]]+1;
mx[k]=max(max(mx[ch[k][0]],mx[ch[k][1]]),key[k]);
}
void rotate(int &y,int md){
int x=ch[y][md];
ch[y][md]=ch[x][!md];
ch[x][!md]=y;
pushup(y);
pushup(x);
y=x;
}
void insert(int &k,int v,int x){
if(!k){
k=++cnt;
val[k]=v;
key[k]=mx[k]=x;
rnd[k]=rand();
siz[k]=1;
return;
}
siz[k]++;
if(v==val[k]){
key[k]=max(key[k],x);
}else if(v<val[k]){
insert(ch[k][0],v,x);
if(rnd[ch[k][0]]>rnd[k]){
rotate(k,0);
}
}else{
insert(ch[k][1],v,x);
if(rnd[ch[k][1]]>rnd[k]){
rotate(k,1);
}
}
pushup(k);
}
int query(int x){
int k=rt,res=0;
while(k){
if(x==val[k]){
res=max(res,key[k]);
break;
}else if(x<val[k]){
k=ch[k][0];
}else{
res=max(res,max(key[k],mx[ch[k][0]]));
k=ch[k][1];
}
}
return res;
}
}t[N];
inline int lowbit(int x){
return x&(-x);
}
void add(int i,int j,int v){
while(i<=k){
t[i].insert(t[i].rt,j,v);
i+=lowbit(i);
}
}
int qmax(int i,int j){
int res=0;
while(i){
res=max(res,t[i].query(j));
i-=lowbit(i);
}
return res;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
maxn[i]=minn[i]=a[i];
}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
maxn[x]=max(maxn[x],y);
minn[x]=min(minn[x],y);
k=max(k,maxn[x]);
}
f[1]=1;
add(maxn[1],a[1],1);
for(int i=2;i<=n;i++){
f[i]=qmax(a[i],minn[i])+1;
add(maxn[i],a[i],f[i]);
ans=max(ans,f[i]);
}
printf("%d\n",ans);
return 0;
}