题目描述
给出N个正整数a[1..N],再给出K个关系符号(>、<或=)s[1..k]。
选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1]。
求出L的最大值。
输入
第一行两个正整数,分别表示N和K (N, K <= 500,000)。
第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6)。
第三行给出K个空格隔开关系符号(>、<或=),第i个表示s[i]。
输出
一个正整数,表示L的最大值。
样例输入
7 3 2 4 3 1 3 5 3 < > =
样例输出
6
提示
选出的子序列为2 4 3 3 5 3,相邻大小关系分别是< > = < >。
【题解】
喜闻乐见的数据结构优化dp。看完了题,在演草纸上写了一句“动规吧?”,然后就把这句话放一边了。觉得二分应该能行,就写了个二分。怎么check呢?写了个dfs。后来跟wzz说这个事,大佬非常不理解地说:还不如直接深搜呢,白白地加个log。瞬间觉得好有道理啊,果然上午不知道干了些什么。
下午听讲题,自己连O(n^2)的转移方程都没写出来,简直了。正解思路很清奇,f[i]表示到i这一位最长的子序列长度,有了长度符号就定下来了。不等号按符号维护线段树,等号直接拿数组标记一下。线段树的下标是a[i]的权值,内容是f[i]的权值,这样区间查询满足不等号的最大值就非常快了。得出f[i]之后推出下一位的符号,更新相应的线段树或数组即可。
线段树是个好东西。上次方伯伯的玉米田用到了树状数组优化dp,这一次算是第二道数据结构优化dp。虽然在理论上来讲树状数组比线段树好打多了,可我始终还是更喜欢线段树。连续两天的dp题都是想过它是dp,但还是没按dp来做。学完一段时间以后dp能力下降得多了,想当初正在讲的时候也是不怕想正解的。原来以为自己不擅长数学和数据结构,现在看来dp也不是很行,集训要做的事还太多了啊。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int sj=500010; char fh[sj]; int n,k,a[sj],f[sj],dh[1000010],temp,jg,jd; void bj(int &x,int y) { x=x>y?x:y; } struct Tree { int l,r,jz; }ds[1000010*4],xs[1000010*4]; void bt1(int x,int y,int z) { ds[x].l=y; ds[x].r=z; if(y==z) return; int mid=(y+z)>>1; bt1(x<<1,y,mid); bt1((x<<1)|1,mid+1,z); } void bt2(int x,int y,int z) { xs[x].l=y; xs[x].r=z; if(y==z) return; int mid=(y+z)>>1; bt2(x<<1,y,mid); bt2((x<<1)|1,mid+1,z); } int query1(int x,int y,int z) { if(ds[x].l==y&&ds[x].r==z) return ds[x].jz; int mid=(ds[x].l+ds[x].r)>>1; if(mid<y) return query1((x<<1)|1,y,z); if(z<=mid) return query1(x<<1,y,z); return max(query1(x<<1,y,mid),query1((x<<1)|1,mid+1,z)); } int query2(int x,int y,int z) { if(xs[x].l==y&&xs[x].r==z) return xs[x].jz; int mid=(xs[x].l+xs[x].r)>>1; if(mid<y) return query2((x<<1)|1,y,z); if(z<=mid) return query2(x<<1,y,z); return max(query2(x<<1,y,mid),query2((x<<1)|1,mid+1,z)); } void update1(int x,int y,int z) { if(ds[x].l==ds[x].r&&ds[x].r==z) { ds[x].jz=y; return; } int mid=(ds[x].l+ds[x].r)>>1; if(mid<z) update1((x<<1)|1,y,z); if(z<=mid) update1(x<<1,y,z); ds[x].jz=max(ds[x<<1].jz,ds[(x<<1)|1].jz); } void update2(int x,int y,int z) { if(xs[x].l==xs[x].r&&xs[x].r==z) { xs[x].jz=y; return; } int mid=(xs[x].l+xs[x].r)>>1; if(mid<z) update2((x<<1)|1,y,z); if(z<=mid) update2(x<<1,y,z); xs[x].jz=max(xs[x<<1].jz,xs[(x<<1)|1].jz); } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); bj(jd,a[i]); } for(int i=1;i<=k;i++) cin>>fh[i]; bt1(1,1,jd); bt2(1,1,jd); for(int i=1;i<=n;i++) { if(dh[a[i]]+1>f[i]) f[i]=dh[a[i]]+1; if(a[i]!=jd) { temp=query1(1,a[i]+1,jd); if(temp+1>f[i]) f[i]=temp+1; } if(a[i]!=1) { temp=query2(1,1,a[i]-1); if(temp+1>f[i]) f[i]=temp+1; } temp=f[i]%k; if(!temp) temp=k; if(fh[temp]==‘=‘) dh[a[i]]=f[i]; if(fh[temp]==‘>‘) update1(1,f[i],a[i]); if(fh[temp]==‘<‘) update2(1,f[i],a[i]); bj(jg,f[i]); } printf("%d",jg); return 0; }