[SCOI2014]方伯伯的玉米田題解
阿新 • • 發佈:2022-12-03
[SCOI2014]方伯伯的玉米田
題目描述
方伯伯在自己的農田邊散步,他突然發現田裡的一排玉米非常的不美。這排玉米一共有 \(N\) 株,它們的高度參差不齊。方伯伯認為單調不下降序列很美,所以他決定先把一些玉米拔高,再把破壞美感的玉米拔除掉,使得剩下的玉米的高度構成一個單調不下降序列。方伯伯可以選擇一個區間,把這個區間的玉米全部拔高 \(1\) 單位高度,他可以進行最多 \(K\) 次這樣的操作。拔玉米則可以隨意選擇一個集合的玉米拔掉。問能最多剩多少株玉米,來構成一排美麗的玉米。
輸入格式
第一行包含兩個整數 \(n, K\),分別表示這排玉米的數目以及最多可進行多少次操作。第二行包含 \(n\)
輸出格式
輸出一個整數,最多剩下的玉米數。
樣例 #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; }