Description
给出N个正整数a[1..N],再给出K个关系符号(>、<或=)s[1..k]。
选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1]。
求出L的最大值。
N, K <= 500,000
a[i] <= 10^6
Solution
这题写了有双倍经验欸( •̀ ω •́ )y
n^3的dp是比较好想的,数据结构可以搞到n^2logn但是没甚么luan用
去掉一维后设f[i]表示序列a[]做到第i位的最长答案,我们可以通过f[i]推出符号是b[(f[i]-1)%k+1],进而可以更新后面的值
那么根据符号把f分3类后,”=”用桶维护,”>”和”<”用权值线段树维护
可以证明这样设是正确的,即一个点的最优值一定转移自钱一个点的最优值
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const int N=1000005;
int f[N],w[N],a[N],b[N];
int max[N*4][2];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void modify(int now,int tl,int tr,int x,int v,int opt) {
max[now][opt]=std:: max(max[now][opt],v);
if (tl==tr) return ;
int mid=(tl+tr)>>1;
if (x<=mid) modify(now<<1,tl,mid,x,v,opt);
else modify(now<<1|1,mid+1,tr,x,v,opt);
}
int query(int now,int tl,int tr,int l,int r,int opt) {
if (l>r) return 0;
if (tl==l&&tr==r) return max[now][opt];
int mid=(tl+tr)>>1;
if (r<=mid) return query(now<<1,tl,mid,l,r,opt);
if (l>mid) return query(now<<1|1,mid+1,tr,l,r,opt);
int qx=query(now<<1,tl,mid,l,mid,opt);
int qy=query(now<<1|1,mid+1,tr,mid+1,r,opt);
return std:: max(qx,qy);
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
int n=read(),m=read(),max=0;
rep(i,1,n) {
a[i]=read();
max=std:: max(a[i],max);
}
rep(i,1,m) {
char sym=getchar();
for (;sym!='<'&&sym!='>'&&sym!='=';sym=getchar());
if (sym=='<') b[i]=0;
else if (sym=='>') b[i]=1;
else b[i]=2;
}
int ans=0;
rep(i,1,n) {
f[i]=std:: max(std:: max(query(1,1,max,1,a[i]-1,0),query(1,1,max,a[i]+1,max,1)),w[a[i]])+1;
ans=std:: max(ans,f[i]);
int sym=b[(f[i]-1)%m+1];
if (sym==2) w[a[i]]=std:: max(w[a[i]],f[i]);
if (sym==0) modify(1,1,max,a[i],f[i],0);
if (sym==1) modify(1,1,max,a[i],f[i],1);
}
printf("%d\n", ans);
return 0;
}