洛谷 P3287 [SCOI2014] 方伯伯的玉米田 题解
题目大意
给定一个长度为 \(N\) 的序列 \(a\),可以进行最多 \(K\) 次操作,每次操作可以选择一个区间加 \(1\)。
求操作之后最长的最长不降升子序列长度。
\(1\le N\le 10^4\),\(1\le K \le 500\),\(1\le a_i\le 5000\)。
题目解析
要让最长不降升子序列最长,那么显然要让后面的数更大,所以每次操作的右端点选择 \(N\) 肯定不劣。
设 \(f(i,j)\) 为前 \(i\) 个 LIS 为 \(i\),使用了 \(k\) 次操作的最长 LIS 长度。
假设从 \(k\) 转移,如果 \(a_k\le a_i\),直接转移,否则使用操作把 \(a_i\) 变成和 \(a_k\) 一样大即可。
\[f(i,j)=\max\limits_{1\le k < i}
\left\{
\begin{aligned}
f(k,j)+1 & & a_k\le a_i\\
f(k,j+a_i-a_k)+1 & & a_k>a_i
\end{aligned}
\right.
\]
朴素刷表转移为 \(O(N^2K)\)。
考虑优化。
第一种转移,转移前后的 \(j\) 相同,每次从更小或者相同的 \(a_i\) 转移,对于每个 \(j\) 维护 \(low(j,V)=\max\limits_{a_i\le V} f(i,j)\)。
第二种转移,转移前后的 \(a_i+j\) 相同,每次从更小或者相同的 \(j\) 转移,对于每个 \(a[i]+j\) 维护 \(over(A,B)=\max\limits_{a_i+j=A,j\le B}f(i,j)\)
开若干个树状数组维护前缀最大值即可。
时间复杂度 \(O(nk\log a_i)\)。
#define maxn 10039
int n,m,k,a[maxn],f[maxn][539],ans;
class BIT{
public:
#define lowbit(x) (x&-x)
int c[5539];
int query(int x){ int s=-1; while(x) s=mmax(s,c[x]),x-=lowbit(x); return s; }
void add(int x,int y){ while(x<=m) c[x]=mmax(c[x],y),x+=lowbit(x); return; }
}lo[539],ov[5539],sum;
int main(){
fin>>n>>k; int i,j; for(i=1;i<=n;i++) fin>>a[i],m=mmax(a[i],m);
for(i=1;i<=n;i++) for(j=0;j<=k;j++){
f[i][j]=mmax(lo[j].query(a[i]),ov[a[i]+j].query(j+1))+1;
ans=mmax(ans,f[i][j]);
lo[j].add(a[i],f[i][j]);
ov[a[i]+j].add(j+1,f[i][j]);
}
fin<<ans<<'\n';
return 0;
}