1 条题解

  • 0
    @ 2026-5-9 16:24:28

    dp

    状态

    容易想到一个状态 f(i,j)f(i,j) 表示前 ii 株玉米,进行 jj 次操作且第 ii 株玉米不拔掉最多剩多少株玉米。

    转移

    f(i,j)=maxk<i,lj,ak+lai+jf(k,l)+1f(i,j)=\max_{k<i,l\le j,a_k+l\le a_i+j}f(k,l)+1

    可以用暴力来枚举 k,lk,l 进行转移。

    for (int k = 1; k < i; ++k)
    	for (int l = 0; l <= j; ++l)
    		if (a[k] + l <= a[i] + j) f[i][j] = max(f[i][j], f[k][l] + 1);
    

    条件中的 k<ik<i 其实可以先不用管,看到 lj,ak+lai+jl\le j,a_k+l\le a_i+j 明显是个二维的前缀和,于是就可以用二维的树状数组来维护。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 10005;
    
    int n, K, m, a[N], f[N][505], c[5505][505];
    
    inline void add(const int &x, const int &y, const int &d) {
    	for (int i = x; i <= m; i += i & ~i + 1)
    		for (int j = y; j <= K + 1; j += j & ~j + 1) c[i][j] = max(c[i][j], d);
    }
    inline int ask(const int &x, const int &y) {
    	int res = 0;
    	for (int i = x; i; i &= i - 1)
    		for (int j = y; j; j &= j - 1) res = max(res, c[i][j]);
    	return res;
    }
    
    int main() {
    	scanf("%d%d", &n, &K);
    	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), m = max(m, a[i]); m += K;
    	for (int i = 1; i <= n; ++i)
    		for (int j = K; ~j; --j)//这里注意要倒着循环,因为小的j的更新会影响到大的。
    			add(a[i] + j, j + 1, f[i][j] = ask(a[i] + j, j + 1) + 1);
    	printf("%d", ask(m, K + 1));
    	return 0;
    }
    

    时间复杂度 O(nklogklog(k+maxai))O(nk\log k \log(k+\max a_i))。其实这样就能 AC 了,但是还能优化。

    可以发现在求二维前缀和时,每一行从左到右是单调不降的,每一列从上到下也是单调不降的。所以对于 f(i,j)f(i,j) 最大值只会在第 ai+ja_i+j 行和第 jj 列里,于是只用建一个 k+maxaik+\max a_i 行和一个 kk 行的一维树状数组记一下此行和此列的最大值进行更新转移就行了。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 10005;
    
    int n, K, m, a[N], c[2][5505][505];
    
    inline void add(const int &x, const int &y, const int &d) {
    	for (int i = x; i <= m; i += i & ~i + 1) c[0][i][y] = max(c[0][i][y], d);
    	for (int i = y; i <= K + 1; i += i & ~i + 1) c[1][x][i] = max(c[1][x][i], d);
    }
    inline int ask(const int &x, const int &y) {
    	int res = 0;
    	for (int i = x; i; i &= i - 1) res = max(res, c[0][i][y]);
    	for (int i = y; i; i &= i - 1) res = max(res, c[1][x][i]);
    	return res;
    }
    
    int main() {
    	scanf("%d%d", &n, &K);
    	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), m = max(m, a[i]); m += K;
    	for (int i = 1; i <= n; ++i)
    		for (int j = K; ~j; --j)//这里正序倒序循环都没关系
    			add(a[i] + j, j + 1, ask(a[i] + j, j + 1) + 1);
    	printf("%d", ask(m, K + 1));
    	return 0;
    }
    

    时间复杂度 O(nklog(k+maxai))O(nk\log(k+\max a_i))

    • 1

    信息

    ID
    5259
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者