P5797 [SEERC 2019] Max or Min 题解

闲话

@masonxiong 在我们学校组织的模拟赛中场切了这道题,大家快去膜拜他!

题解

先考虑固定 \(x\)

固定 \(x\) 后,实际上整个数组可以分成三大类:\(a_i<x\)\(a_i=x\)\(a_i>x\)

首先容易发现,每个 \(a_i=x\)\(i\) 相当于是把一个环(或者一个链)破坏成更多的链的断点。换句话说,对于 \(a_i=x\)\(i\)\(i\) 相邻两边的链是互不影响的。因此我们可以把环拆分成若干个链计算答案。

其次我们观察到,如果我们只要 \(x\) 的答案,实际上在数组中,我们只需要知道一个数关于 \(x\) 的大小关系即可。

具体来讲,我们定义一个数组 sgn,其中 \(sgn_i=\begin{cases}1 & a_i>x \\ 0 & \text{otherwise.}\end{cases}\)

这样我们可以简化计算。

接着,我们考虑每一个链的性质。

  • 对于一个全是 \(1\) 的链,答案一定是链的长度。全是 \(0\) 也成立,因为只需要在链的边界开始一直使用 \(\min\) 或者 \(\max\) 操作即可。
  • 对于一个 \(01\) 交替的链,设链的长度为 \(len\),那么答案是 \(len+\lfloor \frac{len}{2} \rfloor\)。对于这种情况,我们需要把 \(0\) 或者 \(1\) 全部转化成 \(1\) 或者 \(0\),最少需要 \(\lfloor \frac{len}{2} \rfloor\) 次,然后还需要变成 \(x\),所以答案就是 \(len+\lfloor \frac{len}{2} \rfloor\)
  • 对于一个一般的链,这个链一定可以拆成若干个极长 \(01\) 交替的链,和若干个全 \(0\) 和全 \(1\) 的链:比如 001010111101000,我可以把它拆成 0|010101|11|1010|00,然后对于每个链分别计算答案即可。

现在我们会计算对于一个 \(x\) 的答案了。

然后考虑 \(x\) 的变化。

在最开始,我们让 \(sgn\) 全是 \(1\)。当 \(x\) 增加的时候,我们让所有 \(a_i=x\)\(i\)\(sgn_i=0\),表示 \(a_i\) 比以后的 \(x\) 都小。我们需要快速计算对于 \(sgn\) 的所有极长 \(01\) 交替链的 \(\lfloor \frac{len}{2} \rfloor\) 之和,并且支持单点修改。

考虑用线段树维护。

首先考虑节点要维护什么信息。

  1. 极长 \(01\) 交替链的最长前缀长度。(记为ld
  2. 极长 \(01\) 交替链的最长后缀长度。(记为 rd
  3. 所有极长 \(01\) 交替链的 \(\lfloor \frac{len}{2} \rfloor\) 之和。(记为val

其次考虑区间合并。(也就是 pushup

设左区间为 \([l,m]\),右区间为 \([m+1,r]\),不难想到有两种情况:

  • 左区间 \([l,m]\) 的最右节点 \(sgn_m\) 与右区间的最左节点 \(sgn_{m+1}\) 不同。

    此时左区间的 rd 要与右区间的 ld 合并。合并的 \(val\) 需要加上左区间和右区间的 \(val\) 之后,减去原来的左区间的 \(\lfloor \frac{rd}{2} \rfloor\) 和右区间的 \(\lfloor \frac{ld}{2} \rfloor\) 的贡献之和,再加上合并后的贡献 \(\lfloor \frac{ld+rd}{2} \rfloor\)

    特判前缀或后缀是整个区间的情况。若左区间 rd 是整个区间,那么合并后的区间的 ld 要加上右区间的 ld。对于右区间同理。

  • 左区间 \([l,m]\) 的最右节点 \(sgn_m\) 与右区间的最左节点 \(sgn_{m+1}\) 相同。说明不需要进行合并。合并的 \(val\) 需要加上左区间和右区间的 \(val\) 即可。

查询操作的合并区间也与 pushup 类似。对于每一个数 \(x\),把每段区间的查询结果累加,得到 \(res\),用 \(n\) 减去 \(x\) 在数组中出现的次数,再加上 \(res\) 就是答案了。

细节

对于每一个数 \(x\),我们都会开一个 vector 来存储数 \(x\) 在数组中出现的所有下标。注意需要排序。

在代码中,我们一般用长为 \(2n\) 的数组来等效一个环。因此线段树长度为 \(2n\)。在对于 \(x\) 查询一个链的时候,只需要查到下标为 \(n\) 的区间即可。

更多细节看代码吧。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
constexpr ll maxn=4e5+5;
ll arr[maxn],sgn[maxn],n,m;
vector<ll> cur[maxn];
struct node{
    ll ld,rd,val;
}T[maxn<<2];
node pushup(node L,node R,ll l,ll r,ll m){
    node ret={0,0,0}; 
    bool flg=sgn[m]^sgn[m+1];
    ret.ld=L.ld+(L.ld==m-l+1 && flg)*R.ld;
    ret.rd=R.rd+(R.rd==r-m && flg)*L.rd;
    if(flg){
        ret.val=L.val+R.val-(L.rd>>1)-(R.ld>>1)+((L.rd+R.ld)>>1);
    }else{
        ret.val=L.val+R.val;
    }
    return ret;
}
void build(ll l,ll r,ll rt){
    if(l==r){
        T[rt]={1,1,0};
        return;
    }
    ll m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    T[rt]=pushup(T[rt<<1],T[rt<<1|1],l,r,m);
}
void update(ll l,ll r,ll x,ll rt){
    if(l==r && r==x){
        return;
    }
    ll m=(l+r)>>1;
    if(x<=m)update(l,m,x,rt<<1);
    if(m<x)update(m+1,r,x,rt<<1|1);
    T[rt]=pushup(T[rt<<1],T[rt<<1|1],l,r,m);
}
node query(ll l,ll r,ll L,ll R,ll rt){
    if(L<=l && r<=R){
        return T[rt];
    }
    ll m=(l+r)>>1;
    if(R<=m)return query(l,m,L,R,rt<<1);
    else if(m<L)return query(m+1,r,L,R,rt<<1|1);
    else return pushup(query(l,m,L,R,rt<<1),query(m+1,r,L,R,rt<<1|1),max(l,L),min(r,R),m);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>arr[i];
        cur[arr[i]].push_back(i);
        cur[arr[i]].push_back(n+i);
    }
    for(int i=1;i<=m;i++){
        sort(cur[i].begin(),cur[i].end());
    }
    for(int i=1;i<=(n<<1);i++)sgn[i]=1;
    build(1,(n<<1),1);
    for(int i=1;i<=m;i++){
        if(!cur[i].size()){
            cout<<-1<<" ";
            continue;
        }
        for(auto j:cur[i])sgn[j]^=1;
        for(auto j:cur[i]){
            update(1,(n<<1),j,1);
        }
        ll res=0;
        for(int j=0;j<cur[i].size()-1;j++){
            if(cur[i][j]==cur[i][j+1]-1)continue;
            if(cur[i][j]>n)break;
            res+=query(1,(n<<1),cur[i][j]+1,cur[i][j+1]-1,1).val;
        }
        cout<<n-cur[i].size()/2+res<<" ";
    }
    return 0;
}
posted @ 2025-06-16 18:21  reinforest  阅读(37)  评论(0)    收藏  举报