洛谷 P3928 Sequence
题目描述
小强喜欢数列。有一天,他心血来潮,写下了三个长度均为n的数列。
阿米巴也很喜欢数列。但是他只喜欢其中一种,波动数列。
阿米巴把他的喜好告诉了小强。小强便打算找出这三个数列内的最长波动数列。
也就是说,如果我们将三个数列记做a[n][3],他必须要构造一个二元组序列:<p[i], q[i]>,使得对于任何 i>1 有:
p[i] > p[i-1]
若q[i] = 0,a[p[i]][q[i]] >= a[p[i-1]][q[i-1]]
若q[i] = 1,a[p[i]][q[i]] <= a[p[i-1]][q[i-1]]
若q[i] = 2,只要保持段内同向即可(就是对于连续的一段q[i]=2,要么都有a[p[i]][q[i]] >= a[p[i-1]][q[i-1]],要么都有a[p[i]][q[i]] <= a[p[i-1]][q[i-1]])。
小强希望这个二元组序列尽可能长。
提示:当q[i] != q[i-1]时,数列的增减性由q[i]而非q[i-1]决定。
清晰版题目描述
小强拿到一个3×n的数组,要在每一列选一个数(或者不选),满足以下条件:
1.如果在第一行选,那它必须大于等于上一个数
2.如果在第二行选,那么必须小于等于上一个数
3.如果在第三行选,对于连续的一段在第三行选的数,必须满足方向相同(都小于等于上一个数或者都大于等于上一个数)
清晰版描述简直坑人。。。
思路非常明显,智障dp。状态转移方程不读错题的话简直秒出。之后考虑转移优化。显然,一个范围内的最大值可以用线段树维护,当然要事先吧数据离散化。代码很简单,二十分钟就可以敲完,然后我就爆了一个钟的空间23333
#include<bits/stdc++.h> using namespace std; #define MAXN 1000000+10 typedef long long LL; int n,tot=0,ans=0,pos[4][MAXN],dp[5][MAXN],tr[5][MAXN*52]; LL a[5][MAXN],b[MAXN*4]; void pushup(int t,int k){tr[t][k]=max(tr[t][k<<1],tr[t][k<<1|1]);} void build(int t,int k,int l,int r){ tr[t][k]=0; if(l==r)return; int mid=(l+r)>>1; build(t,k<<1,l,mid); build(t,k<<1|1,mid+1,r); } void update(int t,int k,int l,int r,int p,int val){ if(l==r&&l==p){ tr[t][k]=val; return; } int mid=(l+r)>>1; if(p<=mid)update(t,k<<1,l,mid,p,val); else update(t,k<<1|1,mid+1,r,p,val); pushup(t,k); } int query(int t,int k,int l,int r,int L,int R){ if(l>=L&&r<=R)return tr[t][k]; int mid=(l+r)>>1; if(R<=mid)return query(t,k<<1,l,mid,L,R); else if(L>mid)return query(t,k<<1|1,mid+1,r,L,R); else return max(query(t,k<<1,l,mid,L,R),query(t,k<<1|1,mid+1,r,L,R)); } int main(){ //freopen("data.in","r",stdin); scanf("%d",&n); for(int i=1;i<=3;i++) for(int j=1;j<=n;j++){ scanf("%lld",&a[i][j]); b[++tot]=a[i][j]; } sort(b+1,b+tot+1); tot=unique(b+1,b+tot+1)-b,tot--; for(int i=1;i<=4;i++)build(i,1,1,tot); for(int i=1;i<=3;i++) for(int j=1;j<=n;j++) pos[i][j]=lower_bound(b+1,b+tot+1,a[i][j])-b; for(int i=1;i<=4;i++)update(i,1,1,tot,pos[i==4?3:i][1],1),dp[i][1]=1; for(int i=2;i<=n;i++){ for(int k=1;k<=4;k++)dp[k][i]=1; for(int k=1;k<=4;k++){ if(k==1) for(int j=1;j<=4;j++) dp[k][i]=max(dp[k][i],query(j,1,1,tot,1,pos[1][i])+1); else if(k==2) for(int j=1;j<=4;j++) dp[k][i]=max(dp[k][i],query(j,1,1,tot,pos[2][i],tot)+1); else if(k==3) for(int j=1;j<=3;j++) dp[k][i]=max(dp[k][i],query(j,1,1,tot,pos[3][i],tot)+1); else for(int j=1;j<=4;j++) if(j!=3)dp[k][i]=max(dp[k][i],query(j,1,1,tot,1,pos[3][i])+1); } for(int k=1;k<=4;k++){ update(k,1,1,tot,pos[k==4?3:k][i],dp[k][i]); ans=max(ans,dp[k][i]); } } printf("%d\n",ans); return 0; }
Hello World