洛谷P3287 [SCOI2014] 方伯伯的玉米田 (二维树状数组+dp枚举)

原题链接

题解

难点一:区间右端点的确定

    首先,一个拔高区间的右端点一定是最右端n,接下来假设区间 [ L , R ] L>1 && R<n 我们按照左右区间情况讨论

  1、对于区间左边而言——从左边到右,区间对于左侧的区间贡献要么为 0 要么 正贡献

  2、对于区间右边而言——从区间到右边,区间拔高后,对于右侧区间的贡献要么为 0 要么 负贡献

综上区间的右端点一定是n

难点二:dp状态设计

  接下来我们设计dp状态 dp【i】【j】——以 i 为结尾,拔高 j 次的ans,转移方程为:

  dp[i][j]=max{dp[k][l]}+1(1<=k<i,0<=l<=j,h[i]+j>=h[k]+l)

   每次暴力枚举显然超时,我们考虑用二维树状数组优化——虽然树状数组只能维护不差分信息,但对于MAX信息二维树状数组我们可以查到从(1,1)到(i,j)矩形的MAX值;但仍有一个问题,就是h[i]+j>=h[k]+l 的信息怎么维护。

  我们修改二维树状数组的含义,行坐标含义——玉米下标1~i ;列坐标含义——玉米拔高后的高度即h[k]+l

剩余细节详见code

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define rept(i,a,b) for (int i=(a);i<(b);i++)
#define drep(i,a,b) for (int i=(a);i>=(b);i--)
const int N=1e4+5;
const int M=320;
const int mod=998244353;
const int INF=1e9+5;
const __int128 ONE=1;
mt19937_64 rng(time(0)); //更良好的随机数生成 范围unsigned long long 
//二维树状数组+dp枚举
int a[N];
int dp[N][505];
int tr[N][505];

int lowbit(int x){
    return -x&x;
}
//此处取MAX这种不可差分信息是因为每次矩阵都从左上角点开始
int Query(int x,int y){
    y++;//y可能取到0
    int ans=0;
    for (int i=x;i>0;i-=lowbit(i)){
        for (int j=y;j>0;j-=lowbit(j)) ans=max(tr[i][j],ans);
    }
    return ans;
}

void Add(int x,int y,int v){
    y++;//y可能取到0
    for (int i=x;i<=10000;i+=lowbit(i)){
        for (int j=y;j<=501;j+=lowbit(j)) tr[i][j]=max(tr[i][j],v);
    }
}

void solve(){
    int n,K;
    cin>>n>>K;
    rep(i,1,n) cin>>a[i];

    int ans=0;
    rep(i,1,n){
        drep(j,K,0){
            dp[i][j]=Query(a[i]+j,j)+1;
            Add(a[i]+j,j,dp[i][j]);
            ans=max(ans,dp[i][j]);
        }
    }
    cout<<ans<<"\n";
}

int main(){
    // freopen("input.txt","r",stdin);
    // freopen("output.txt","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int t=1;
    // cin>>t;
    while (t--){
        solve();
    }
    return 0;
}

 

posted @ 2025-12-07 21:13  黑屿白  阅读(2)  评论(0)    收藏  举报