题解:P11187 配对序列
大家好,我非常喜欢线段树优化 dp,于是我使用线段树优化 dp 通过了这道题目。
前排提醒:本题解做法时间复杂度带一只 ,如果想要学习 做法请自行略过。
Solution P11187
Idea
不难发现配对序列就是形如 的序列。
我们设 为当前最后一个数为 ,这个数出现在第奇数个位置(表示为 )还是在第偶数个位置(表示为 )的最长配对序列的长度。
显然有:,。
解释一下:如果这个数是第偶数个位置,它前面一个必须是它自己。如果是第奇数个位置,只要前一个位置不是它自己都可以转移。另外, 是题目中给定的 的最大值。
这样的复杂度是 的,瓶颈在于 的转移。
考虑优化。
我们发现后面转移 的式子实际上就是 与 两个区间最大值的最大值。
求区间最大值,不难联想到线段树。
这样我们把 的信息维护到线段树里,开一个数组记录 ,然后就可以转移了。
最后答案显然是 (),线段树上其实就是 号区间,所以输出 即可( 为维护线段树的数组)。
这样复杂度就是只带一只 的了。
Code
代码中的 数组相当于记录上文中 的数组。
#include<bits/stdc++.h>
#define ls now<<1
#define rs (now<<1)|1
using namespace std;
const int N=500005;
int dp[N],tr[N<<2],n,a[N];
void add(int l,int r,int p,int now,int x){
if(l==r&&l==p){
tr[now]=max(tr[now],x);
return;
}
int mid=(l+r)>>1;
if(p<=mid)add(l,mid,p,ls,x);
else add(mid+1,r,p,rs,x);
tr[now]=max(tr[ls],tr[rs]);
}
int query(int l,int r,int al,int ar,int now){
if(al>ar)return 0;
if(al<=l&&r<=ar)return tr[now];
int mid=(l+r)>>1,res=0;
if(al<=mid)res=max(res,query(l,mid,al,ar,ls));
if(ar>mid)res=max(res,query(mid+1,r,al,ar,rs));
return res;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
if(dp[a[i]]!=0)add(1,500000,a[i],1,dp[a[i]]+1);
int res=max(query(1,500000,1,a[i]-1,1),query(1,500000,a[i]+1,500000,1));
dp[a[i]]=res+1;
}
printf("%d",tr[1]);
}

浙公网安备 33010602011771号