【YbtOJ#20061】波动序列
题目
题目链接:http://noip.ybtoj.com.cn/contest/86/problem/3
思路
设 \(f[i][j][1/2/3/4]\) 表示前 \(i\) 个数,最后一个数选的是 \(j\),且最后一个数是在第一行 / 第二行 / 第三行升序 / 第三行降序的最大选中个数。
容易推出 \(O(nm)\) 转移。发现转移中对于每一个不同的列 \(i\),只有 \(4\) 个位置需要修改,所以开四棵线段树维护 dp 值即可。
每次转移只需要区间求 \(\max\),单点修改。时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m,a[4][N],b[N*3];
int read()
{
int d=0,f=1; char ch=getchar();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d*f;
}
struct SegTree
{
int maxn[N*12];
void update(int x,int l,int r,int k,int v)
{
maxn[x]=max(maxn[x],v);
if (l==k && r==k) return;
int mid=(l+r)>>1;
if (k<=mid) update(x*2,l,mid,k,v);
else update(x*2+1,mid+1,r,k,v);
}
int query(int x,int l,int r,int ql,int qr)
{
if (l==ql && r==qr) return maxn[x];
int mid=(l+r)>>1;
if (qr<=mid) return query(x*2,l,mid,ql,qr);
if (ql>mid) return query(x*2+1,mid+1,r,ql,qr);
return max(query(x*2,l,mid,ql,mid),query(x*2+1,mid+1,r,mid+1,qr));
}
}seg1,seg2,seg3,seg4;
int Maxquery(int ql,int qr,bool f1,bool f2,bool f3,bool f4)
{
int maxf=0;
if (f1) maxf=max(maxf,seg1.query(1,1,m,ql,qr)+1);
if (f2) maxf=max(maxf,seg2.query(1,1,m,ql,qr)+1);
if (f3) maxf=max(maxf,seg3.query(1,1,m,ql,qr)+1);
if (f4) maxf=max(maxf,seg4.query(1,1,m,ql,qr)+1);
return maxf;
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
n=read();
for (int i=1;i<=3;i++)
for (int j=1;j<=n;j++)
b[++m]=a[i][j]=read();
sort(b+1,b+1+m);
m=unique(b+1,b+1+m)-b-1;
for (int i=1;i<=3;i++)
for (int j=1;j<=n;j++)
a[i][j]=lower_bound(b+1,b+1+m,a[i][j])-b;
for (int i=1;i<=n;i++)
{
int f1=Maxquery(1,a[1][i],1,1,1,1),f2=Maxquery(a[2][i],m,1,1,1,1);
int f3=Maxquery(1,a[3][i],1,1,1,0),f4=Maxquery(a[3][i],m,1,1,0,1);
seg1.update(1,1,m,a[1][i],max(f1,1)); seg2.update(1,1,m,a[2][i],max(f2,1));
seg3.update(1,1,m,a[3][i],max(f3,1)); seg4.update(1,1,m,a[3][i],max(f4,1));
}
printf("%d",Maxquery(1,m,1,1,1,1)-1);
return 0;
}