Luogu4694「PA2013」Raper / CF802O April Fools' Problem (hard)

线段树维护模拟费用流!

这篇题解旨在给 Clarisls 的线段树写一个详细点的解释,原博客参考https://www.cnblogs.com/clrs97/p/5124895.html

思考如果将本题数据范围缩小,放到一个费用流能过的复杂度

一种可行的建图就是建立一虚拟源点限制流量,同时建一排虚点 p1,p2pn

虚源点向虚点连流量 1 费用 ai 的边,虚点向汇点连 (1,bi) 的边,源点向虚源点连 (0,k) 的边,虚点直接从前往后连 (+,0) 的边

剩下要做的是写一个 EK

既然可以使用费用流做,那么必然满足函数是下凸的,即每次选出来的点对的权值和单调 不降

同时一个比较经典的结论是选择 k 个点对所得到的点集是选择 k+1 对点所得到的点集的子集,但是匹配不一定相同,正确性是显然的

(然后把网络流建图扔掉)

考虑贪心选出最小的合法点对,那么问题本质上变成了最小的带权括号匹配,转化后维护序列所选择的左右括号前缀和,最后得到的序列需要满足任意前缀和非负

按照 Clarisls 的博客所写的定义:

  • mina/minb 表示区间里面 a/b 数组最小值所在的位置

  • va 表示区间里面选择 posaposb 的最小合法方案

  • vc 表示区间里面选择 posa>posb 的最小合法方案

  • vb 表示区间里面选择 posa>posb 的最小合法方案,同时满足所选择的两点之间的前缀和的最小值大于区间里面前缀和的最小值

  • tag/mn 表示区间加法懒标记和区间前缀和最小值

  • alim 表示满足 [l,alim1] 区间中前缀和最小值大于整个区间前缀和最小值的 A 中值最小的一个

  • blim 表示满足 [blim,r] 区间中前缀和最小值大于整个区间前缀和最小值的 B 中值最小的一个

注意:alim 定义中区间是不到其本身位置的,但是 blim 中到了,所以最初建线段树时可以给 alim 赋值,但后者不能

push_up 的过程包括:

  • 使用子区间信息直接更新:mina,minb,mn,va,vb,vc

  • 使用 left->mina,right->minb 更新 va

  • 使用 left->minb,right->mina 更新 vc

以上根据含义,正确性显然,下述转移正确性耐人寻味,建议思考

  • 对于 left->mn>right->mn 的情况

    因为左边的最小值大,所以 left->vc 可以更新 vb

    左边的 minb/mina 被解放,可以先后更新 vb/alim

    注意因为是区间交叉时的信息更新,所以左边一定选 B

  • 对于 left->mn<right->mn 的情况

    右边的最小值大,那么 right->vc 可以更新 vb

    右边的 mina/minb 被解放,可以先后更新 vb/blim

    仍然注意因为是区间交叉时的信息更新,右子区间选 A

  • 对于 left->mn=right->mn 的情况

    因为最小值在两边都能取到,只能用 left->minb,right->mina 更新 vb/alim/blim

最后,代码是最好的教程,可以配合食用

inline void push_up(int p){
    #define ls p<<1 
    #define rs p<<1|1
    va[p]=min(min(va[p<<1],va[p<<1|1]),node(mna[p<<1],mnb[p<<1|1])); 
    vc[p]=min(min(vc[p<<1],vc[p<<1|1]),node(mna[p<<1|1],mnb[p<<1]));
    vb[p]=min(vb[p<<1],vb[p<<1|1]);
    if(a[mna[p<<1]]<a[mna[p<<1|1]]) mna[p]=mna[p<<1]; 
    else mna[p]=mna[p<<1|1];
    if(b[mnb[p<<1]]<b[mnb[p<<1|1]]) mnb[p]=mnb[p<<1]; 
    else mnb[p]=mnb[p<<1|1];
    if(mn[ls]>mn[rs]){
        vb[p]=min(vb[p],node(alim[rs],mnb[ls]));
        vb[p]=min(vb[p],vc[ls]);
        if(a[mna[p<<1]]>a[alim[p<<1|1]]) alim[p]=alim[p<<1|1]; else alim[p]=mna[p<<1];
        blim[p]=blim[p<<1|1];
    }else if(mn[ls]<mn[rs]){
        vb[p]=min(vb[p],node(mna[rs],blim[ls]));
        vb[p]=min(vb[p],vc[rs]);
        if(b[blim[p<<1]]>b[mnb[p<<1|1]]) blim[p]=mnb[rs]; else blim[p]=blim[ls];
        alim[p]=alim[p<<1];
    }else{
        vb[p]=min(vb[p],node(alim[rs],blim[ls]));
        alim[p]=alim[ls]; blim[p]=blim[rs];
    } mn[p]=min(mn[p<<1],mn[p<<1|1]); return ;
    #undef ls
    #undef rs
}

实现的时候:

  • 变量维护大多是位置,所以取 min 要注意

  • 可以直接设 A[0]=B[0]=+ ,会方便一些

诶你这不是线段树加速贪心吗?咋跟模拟费用流扯上关系了?

对于选择的括号形如 () 时,是在虚点一排流经了 [l,r] 的点

而如果在这之间选择了 )( 的括号就是将 [pl,pr] 流了反向边,现在的流经情况是 SlplTSprrT

也就是把 [pl,pr] 之间的流量逼了回去

posted @   没学完四大礼包不改名  阅读(136)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示