关于本题 yuanruiqi 线性DP做法的理解
查看原帖
关于本题 yuanruiqi 线性DP做法的理解
750375
Len_zh楼主2024/11/28 18:18

本贴主要针对 yuanruiqi 大佬的 O(n)O(n) 题解做自己的个人理解,可能能解决你的一些疑惑。

特殊性质A

首先是关于 特殊性质A 的 说明,特殊性质A保证只有一个正数。

cp>0c_p>0RiR_i 是第一个满足 aj>aia_j > a_i 且大于 i 的 j 值。

则我们如果 想要拿到 cpc_p 的正贡献,必然有某个区间为 [p,pr](prRp)[p, pr] (pr \ge R_p) 且由于后面全部都是负数,所以不需要取后面,剩下一直区间不动就好了。即区间为 [p,Rp][p, R_p]

在 p 前面的部分,我们又应该尽可能少取,所以我们每次尽可能让数字从左端离开,这时不会记录 sumsum , 所以理想的区间长度每次均为 1。由此可以简单得到 8 分代码

AC做法

这里解释一下题解中的 这个过程启发我们关注队列中只有一个元素的时刻 这句话。

因为有 nn 个区间, 所以 rir_i 可以一个一个地覆盖到 nn ,而且多余的区间可以以重复区间给冲掉,因此实际上可以视为不超过 nn 个区间。

又因为我们从左端退栈不需要计算 sumsum ,所以我们可以设计让多余元素 aia_i 从左端退栈 (前提是他不会被 ai+1a_{i+1} 弹掉。)这样就不会引起其他变化。

这个时候,当我们固定 rnr_n , 我们可以通过构造,使得 ln=rnl_n = r_n 或者 Rln=rnR_{l_n} = r_n , 使得当取到最后的时候,队列中只剩下 rnr_n , 而不引起其他变化。

所以,用 fif_i 来记录栈中只剩下 cic_i 一个元素从而计算 min{fi}\min \left \{ f_i \right \} 作为答案是可行的。

而让 aia_i 计算贡献的方法,一定是他被通过某一个方式弹掉了,这个时候,必然存在某个 rj>Rir_j > R_{i} 。且那个时候,因为 aRia_{R_i} 是第一个大于 aia_i 的,所以当栈中只有 aia_i 时,转移出的 fRif_{R_i} 也一定只含有 aRia_{R_i}

关于第 29 行

第29行有一句

f[i + 1] = max(f[i + 1], f[i] + (a[i + 1] > a[i] ? c[i] : 0));

实际上,a[i]<a[i+1]a[i]<a[i+1] 不需要判断,因为这种情况已经包括在下面的 fpif_{p_i} 计算之下了。实测修改代码为以下代码也可AC

if (a[i]>a[i+1]) f[i+1] = f[i];

感觉这实际上是一种理解的问题,大佬的做法是对 "大于和小于" 的分类讨论,而后者更像是对 a[i]<a[i+1]a[i]<a[i+1] 的特判。

下面是关于8分特性A的代码。
// 以下代码在 #define F(i,l,r) for(int i=l; i<=r; i++) 下运行 

int n;
int a[LXB];
int c[LXB];

void solve()
{
    // 仅有一个正数
    cin>>n;
    F(i,1,n) cin>>c[i];
    F(i,1,n) cin>>a[i];
    int x=0;
    F(i,1,n){
        if (a[i]>a[i-1]) x+=c[i-1]; 
        if (c[i]>0){
            F(j,i+1,n){
                x+=c[j-1];
                if (a[j]>a[i]){
                    cout<<max(0ll,y)<<endl;
                    return ;
                }
            }
            cout<<0<<endl;
            return ;
        }
    }
}





2024/11/28 18:18
加载中...