给定两个长 的序列 和 ,每次操作可以选择一个满足 的 并把 和 改为 ,求任意次操作后 的最大值。
,。
设操作完后的 序列为 。
显然操作相当于每个 不断往外拓展,设 拓展成了 (没有任何 由 拓展来则把 删掉),那么有个结论:
- 一组 合法当且仅当 都有 ,即区间不交且相对顺序和 的相对顺序相同。
那么不妨设 是由 拓展而来的,不难发现 单调递增。
发现钦定 的所有 都满足 为 或 并不影响答案,那么不妨设这两个位置分别为 和 (不存在则为 )。
又发现 可以为 当且仅当 且 中的最大值 ,可以为 同理。那么不妨把所有不合法的 和 都赋值为 。
那么不难发现 对答案有贡献当且仅当:
- 且 ;
- 且 ;
因为 和 已经解决掉所有值域上的限制,只剩下 相对顺序的限制。
那么考虑一个朴素的 dp:设 表示处理完 且 时 的最大值。显然有转移:
发现可以用树状数组维护前缀 max 解决,时间复杂度 。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int S=100005;
int n,a[S],b[S];
int m,tb[S*2];
int top,sta[S],pre[S],nxt[S];
int pos[S*2],lb[S],rb[S];
int c[S];
inline void add(int p,int val)
{
for(int i=p;i<=n;i+=i&-i) c[i]=max(c[i],val);
}
inline int que(int p)
{
int res=0;
for(int i=p;i>=1;i-=i&-i) res=max(res,c[i]);
return res;
}
int main()
{
freopen("copy.in","r",stdin);
freopen("copy.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) tb[++m]=a[i],tb[++m]=b[i];
sort(tb+1,tb+m+1);
m=unique(tb+1,tb+m+1)-tb-1;
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(tb+1,tb+m+1,a[i])-tb;
b[i]=lower_bound(tb+1,tb+m+1,b[i])-tb;
}
sta[top=0]=0;
for(int i=1;i<=n;i++)
{
while(top>0&&a[sta[top]]<a[i]) top--;
pre[i]=sta[top];
sta[++top]=i;
}
sta[top=0]=n+1;
for(int i=n;i>=1;i--)
{
while(top>0&&a[sta[top]]<a[i]) top--;
nxt[i]=sta[top];
sta[++top]=i;
}
for(int i=1;i<=m;i++) pos[i]=-1;
for(int i=1;i<=n;i++)
{
pos[a[i]]=i;
lb[i]=pos[b[i]];
if(lb[i]!=-1&&nxt[lb[i]]<=i) lb[i]=-1;
}
for(int i=1;i<=m;i++) pos[i]=-1;
for(int i=n;i>=1;i--)
{
pos[a[i]]=i;
rb[i]=pos[b[i]];
if(rb[i]!=-1&&pre[rb[i]]>=i) rb[i]=-1;
}
// for(int i=1;i<=n;i++) printf("%d %d\n",lb[i],rb[i]);
for(int i=1;i<=n;i++)
{
if(rb[i]!=-1) add(rb[i],que(rb[i])+1);
if(lb[i]!=-1&&lb[i]!=rb[i]) add(lb[i],que(lb[i])+1);
}
printf("%d\n",que(n));
return 0;
}