【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,lleq 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
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/GJY-JURUO/p/14323523.html