CF 1859E Maximum Monogonosity 绝对值的巧妙处理和状态设计

CF 1859E
题意:给你两个长度为 n n n的数组 a a a, b b b,选择一些点对 ( l i , r i ) (l_i,r_i) (li,ri)
每个点对对答案产生贡献 a b s ( b l i − a r i ) + a b s ( b r i − a l i ) abs(b_{l_i}-a_{r_i})+abs(b_{r_i}-a_{l_i}) abs(bliari)+abs(briali)
任意两点对 ( l i , r i ) ( l j , r j ) (l_i,r_i)(l_j,r_j) (li,ri)(lj,rj)间必须满足 r i < l j r_i<l_j ri<lj或者 r j < l i r_j<l_i rj<li
点对长度为 r i − l i + 1 r_i-l_i+1 rili+1,求选取点对总长度为 k k k时,最大总贡献为多少

朴素写法直接枚举每个当前选到的点,然后枚举当前长度,对当前点进行选与不选的决策,选:直接枚举点对中的 l l l 转移即可;不选:直接从前一个点转移过来,复杂度 O ( n 3 ) O(n^3) O(n3)

这种暴力的做法时间不太够,因为我们在确定点对中 r i r_i ri时,转移时我们必须知道 l i l_i li为多少,才能算出这个点对的贡献。

状态优化:因为这个贡献很特殊,它是绝对值型的,比如我们考虑 a b s ( x − y ) abs(x-y) abs(xy),可以转换为 m a x ( x − y , y − x ) max(x-y,y-x) max(xy,yx),发现 x , y x,y x,y的符号可以多开一个状态来表示,暴力枚举符号,因为绝对值取max所以最后的答案一定最优

所以可以这样做 f x , y , l f_{x,y,l} fx,y,l 表示当前长度为 l l l ( b l i − a r i ) (b_{l_i}-a_{r_i}) (bliari) 的符号状态为 x x x ( b r i − a l i ) (b_{r_i}-a_{l_i}) (briali) 的符号状态为 y y y 且已选取 l i l_i li暂未选择 r i r_i ri时的最大贡献, g l g_l gl表示选择完 r i r_i ri后的最大贡献,也就是统计的答案,因为贡献的两个符号状态我们可以在 f f f转移时就确定,所以 g g g作为答案数组可以省去这两个状态

最后只要枚举当前点,做三件事:
1.选择当前点并作为新的 l i l_i li,确定 x , y x,y x,y,计算 a l i a_{l_i} ali b l i {b_{l_i}} bli的贡献
2.不选择当前点,延续之前确定了的 l i l_i li x , y x,y x,y
3.选择当前点作为 r i r_i ri,计算贡献

void solve(){
    std::vector f(2,std::vector(2,std::vector<ll>(K+1,-inf)));
    std::vector<ll> g(K+1);
    g[0]=0;
    for (int i=1;i<=n;i++){
        for (int x=0;x<2;x++){
            for (int y=0;y<2;y++){
                for (int l=K;l>=1;l--){
                    f[x][y][l]=f[x][y][l-1];
                }
            }
        }

        for (int x=0;x<2;x++){
            for (int y=0;y<2;y++){
                for (int l=0;l<K;l++){
                    f[x][y][l+1]=std::max(f[x][y][l+1],g[l]+(x?-1:1)*b[i]+(y?1:-1)*a[i]);
                }
            }
        }
        for (int x=0;x<2;x++){
            for (int y=0;y<2;y++){
                for (int l=0;l<=K;l++){
                    g[l]=std::max(g[l],f[x][y][l]+(y?-1:1)*b[i]+(x?1:-1)*a[i]);
                }
            }
        }
    }
    std::cout<<g[K]<<"\n";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值