【YBTOJ】【Luogu P3287】[SCOI2014]方伯伯的玉米田

链接:

题目

博客园

题目大意:

可以 \(k\) 次区间加一数列 \(a\),求最长不下降子序列长度。

正文:

最重要的一点是,区间加一操作的右端点一定是 \(n\)。假设原本想要区间 \([l,r]\) 加一,由于是求最长不下降子序列长度,所以 \([l,r]\) 加一后,肯定也小于等于 \([r+1,n]\) 的任意一点,所以直接 \([l,n]\) 对答案没有影响。

接着就是设 \(f_{i,j}\) 表示 \(a_i\) 被加过 \(j\) 次的最长不下降子序列长度,则有:

\[f_{i,j}=\max_{k<i,l\leq j,a_k+l<a_i+j}\{f_{k,l}+1\} \]

可以用二维的树状数组维护。

代码:

int n, mx, m;
int t[M][N], ans;
int a[N];

void update(int x, int k, int val) 
{
	for (; x <= mx; x += x & -x) 
		for (int i = k; i <= m + 1; i += i & -i) 
			t[i][x] = max(t[i][x], val);
}
int query(int x, int k) 
{
	int ans = 0;
	for (; x; x -= x & -x) 
		for (int i = k; i; i -= i & -i) 
			ans = max(t[i][x], ans); 
	return ans;
}

int main()
{
	scanf ("%d%d", &n, &m);
	memset (t, 0, sizeof t);
	for (int i = 1; i <= n; i++)
		scanf ("%d", &a[i]), mx = max(mx, a[i]);
	mx += m; 
	for (int i = 1; i <= n; i++)
		for (int j = m; ~j; j--)
		{
			int tmp = query(a[i] + j, j + 1) + 1;
			ans = max(ans, tmp);
			update(a[i] + j, j + 1, tmp);
		}
	
	printf ("%d\n", ans);
    return 0;
}
posted @ 2021-01-25 09:21  Jayun  阅读(76)  评论(0编辑  收藏  举报