1. 程式人生 > 其它 >[SCOI2014]方伯伯的玉米田題解

[SCOI2014]方伯伯的玉米田題解

[SCOI2014]方伯伯的玉米田

題目描述

方伯伯在自己的農田邊散步,他突然發現田裡的一排玉米非常的不美。這排玉米一共有 \(N\) 株,它們的高度參差不齊。方伯伯認為單調不下降序列很美,所以他決定先把一些玉米拔高,再把破壞美感的玉米拔除掉,使得剩下的玉米的高度構成一個單調不下降序列。方伯伯可以選擇一個區間,把這個區間的玉米全部拔高 \(1\) 單位高度,他可以進行最多 \(K\) 次這樣的操作。拔玉米則可以隨意選擇一個集合的玉米拔掉。問能最多剩多少株玉米,來構成一排美麗的玉米。

輸入格式

第一行包含兩個整數 \(n, K\),分別表示這排玉米的數目以及最多可進行多少次操作。第二行包含 \(n\)

個整數,第 \(i\) 個數表示這排玉米,從左到右第 \(i\) 株玉米的高度 \(a_i\)

輸出格式

輸出一個整數,最多剩下的玉米數。

樣例 #1

樣例輸入 #1

3 1
2 1 3

樣例輸出 #1

3

提示

\(100\%\) 的資料滿足:$2 \le N \lt 10^4 $,\(2 \le K \le 500\)\(1 \leq a_i \leq 5000\)

思路

定義 \(dp[i][j]\) 表示以 \(i\) 結尾,可以拔高 \(j\) 次的最長不下降子序列。

狀態轉移方程:

\[dp[i][j]=\max_{p,q}^{1 \le p < i,0\le q\le j,a[i]+j\ge a[p]+q} dp[p][q]+1 \]

直接暴力轉移顯然不行,時間複雜度 \(O(n^2k^2)\)

,一分沒有。

觀察發現每次拔高後最長不下降子序列的長度不會減少,所以可以用二維樹狀陣列維護 \(dp\) 陣列,但需要改一下狀態。

\(dp[a][b]\) 表示 \([1,i-1]\) 中最多拔高 \(b\) 次,末尾不超過 \(a\) 的最長不下降子序列的長度(類似於 \(n\log n\) 求LIS的 \(low\) 陣列)。

\(dp\) 陣列丟到樹狀數組裡就好了。

時間複雜度 \(O(nk\log n \log k)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
void write(int x){
	if(x<0){putchar('-');x=-x;}
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
int a[10005],f[10005][1005],n,k,maxx,_ans;
//樹狀陣列模板
int lowbit(int x){
	return x&(-x);
}
void change(int x,int y,int val){
	for(int i=x;i<=maxx+k;i+=lowbit(i))
		for(int j=y;j<=k+1;j+=lowbit(j))
			f[i][j]=max(f[i][j],val);
}
int ask(int x,int y){
	int ans=0;
	for(int i=x;i;i-=lowbit(i))
		for(int j=y;j;j-=lowbit(j))
			ans=max(ans,f[i][j]);
	return ans;
}
//---------------
int main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++)a[i]=read(),maxx=max(maxx,a[i]);
	for(int i=1;i<=n;i++){
		for(int j=k;j>=0;j--){
			int t=ask(a[i]+j,j+1)+1;//找到dp[a[i]+j][j+1]
			_ans=max(_ans,t);//更新答案
			change(a[i]+j,j+1,t);//放入樹狀陣列
		}
	}
	printf("%d\n",_ans);
	return 0;
}