1. 程式人生 > 實用技巧 >P3287 [SCOI2014]方伯伯的玉米田

P3287 [SCOI2014]方伯伯的玉米田

首先可以證明,一定存在一種最優解,每次選擇的區間結尾都是 \(n\)。因為如果某一個區間結尾不是 \(n\),將其替換成 \(n\) 仍然保持單調不下降。接著都按這個策略拔高玉米。

\(f_{i,j}\) 表示 \(1\sim i\) 這段字首進行了 \(j\) 次操作,\(i\) 株玉米不被拔掉,所能剩下最多的玉米。

\[f_{i,j}=\max\{f_{p,q}|p<i,q\leq j,a_p+q\leq a_i+j\}+1 \]

列舉 \(i\),剩下兩個限制用二維樹狀陣列維護即可。

有一個小細節,操作次數可能為 \(0\),扔進樹狀陣列時需要加 \(1\)

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

code:

#include<bits/stdc++.h>
using namespace std;
#define Max(x,y)((x)>(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
#define Down(i,x,y)for(i=x;i>=(y);i--)
#define lowbit(x)((x)&-(x))
int c[505][5505],a[10005],k,mx;
int sum(int x,int tmp)
{
	int y,res=0;
	while(x)
	{
		y=tmp;
		while(y)res=Max(c[x][y],res),y-=lowbit(y);
		x-=lowbit(x);
	}
	return res;
}
void add(int x,int tmp,int z)
{
	int y;
	while(x<=k+1)
	{
		y=tmp;
		while(y<=mx+k)c[x][y]=Max(c[x][y],z),y+=lowbit(y);
		x+=lowbit(x);
	}
}
int main()
{
	int n,i,j,tot,ans=0;
	scanf("%d%d",&n,&k);
	For(i,1,n)scanf("%d",&a[i]),mx=Max(mx,a[i]);
	For(i,1,n)
	Down(j,k,0)
	{
		tot=sum(j+1,a[i]+j)+1;
		add(j+1,a[i]+j,tot);
		ans=Max(ans,tot);
	}
	printf("%d",ans);
	return 0;
}