[BZOJ3594][Scoi2014]方伯伯的玉米田(DP+树状数组优化)

本文探讨了一个关于拔高玉米以获得最长不降子序列的问题,并提出了一种利用二维树状数组进行优化的方法。该方法通过状态定义与转移方程,结合树状数组的高效查询与更新特性,实现了O(nKlogalogK)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个结论:每一次拔高玉米的区间的右端点一定是nn
正确性:如果凭空将[x,n]的玉米高度加11,那么最长不降子序列的长度一定会增加。所以当r<n时,操作区间[l,n][l,n]绝对比[l,r][l,r]优。
根据这个结论,可以设一个状态定义:
f[i][j]f[i][j]表示到了第ii个玉米,拔了j次,ii为结尾的最长不降子序列长度。
转移:

f[i][j]=1+maxai+jah+k and jk{f[h][k]}

也就是说,由于上面的结论得到,拔高后第ii个玉米的高度为ai+j,第hh个玉米的高度为ah+k,同时区间[i,n][i,n]被拔高的次数不能是负数。可以由上面两点限制得出转移条件。
还是不足以通过。由于转移条件的特殊性(是AxAy and BxByAx≥Ay and Bx≥By的形式),所以可以使用二维树状数组优化。
具体地,第一维是ai+jai+j,第二维是jj(考虑到第二维j可能为00,因此下标要加1),每计算出一个f[i][j]f[i][j]就将其存入树状数组。
这时候,为了避免f[i][j]f[i][j]f[i][<j]f[i][<j]转移,枚举ff的第二维要从K00倒着枚举。
复杂度O(nKlogalogK)
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Pos(x, k) for (; x <= k; x += x & -x)
#define Neg(x) for (; x; x -= x & -x)
using namespace std;
inline int read()
{
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e4 + 5, M = 555;
int n, K, a[N], f[N][M], T[N][M], ans;
void change(int x, int y, int v)
{
    Pos(x, 7371)
    {
        int z = y;
        Pos(z, 512) T[x][z] = max(T[x][z], v);
    }
}
int ask(int x, int y)
{
    int res = 0;
    Neg(x)
    {
        int z = y;
        Neg(z) res = max(res, T[x][z]);
    }
    return res;
}
int main()
{
    int i, j;
    n = read(); K = read();
    For (i, 1, n) a[i] = read();
    For (i, 1, n) Rof (j, K, 0)
    {
        ans = max(ans, f[i][j] = ask(a[i] + j, j + 1) + 1);
        change(a[i] + j, j + 1, f[i][j]);
    }
    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值