CF484E Sign on Fence

\(\texttt{Description}\)

CodeForces 题目链接

洛谷题目链接

  • 给定一个长度为 \(n\) 的数列 \(A\),有 \(m\) 次询问,每次在给出 \(A_l\sim A_r\) 中选一个长度为 \(k\) 的子区间,使得选出区间的最小值最大。

  • \(1\le n,m\le 10^5\)\(1\le A_i\le 10^9\)

\(\texttt{Solution}\)

首先看到最小值最大,不难想到二分。由于是多组询问,所以考虑整体二分

在整体二分中,我们套路地将所有询问当前答案的值域 \([L,R]\) 分成 \([L,\text{mid}]\)\((\text{mid},R]\) 两部分。 先二分一个 \(\text{mid}\),然后维护所有 \(> \text{mid}\) 的数的最长连续子区间长度。每次询问查询 \([l,r]\) 这一区间的答案。若结果 \(\ge k\),说明答案在 \((\text{mid},R]\) 中,否则在 \([L,\text{mid}]\) 中。然后继续对这两部分递归求解即可。若 \(L=R\),则更新当前递归内的所有询问的答案。

具体来讲,可以参照洛谷 P4513 的思路,使用线段树维护所有 \(> \text{mid}\) 的数的最长连续子区间长度,即把所有 \(>\text{mid}\) 的数的贡献统计到线段树上。考虑在线段树的每个节点中维护 \(\text{len},\text{llen},\text{rlen},\text{sz}\) 四个值,表示该节点的答案、左起最长连续段长度、右起最长连续段长度和该节点对应的区间长度。

\(x\) 为当前节点,\(\text{ls}\) 为左儿子,\(\text{rs}\) 为右儿子。则有:

  • \(\text{llen}_{\text{ls}}\ne \text{sz}_\text{ls}\),说明左起最长连续段不横跨左右儿子的区间\(\text{llen}_x=\text{llen}_{\text{ls}}\);否则说明横跨左右儿子的区间,\(\text{llen}_x=\text{sz}_\text{ls}+\text{llen}_\text{rs}\)\(\text{rlen}_x\) 类似维护即可。

  • \(\text{sz}_x=\text{sz}_\text{ls}+\text{sz}_\text{rs}\)。显而易见。

  • \(\text{len}_x=\max\{\text{len}_\text{ls},\text{len}_\text{rs},\text{rlen}_\text{ls}+\text{llen}_\text{rs}\}\)。表示分别考虑答案完全在左儿子区间内完全在右儿子区间内横跨左右儿子的区间

此外,这题并不像模板题一样答案符合可加性,因为模板题只有数量的限制,可以加加减减。但是这题还有连续的限制,就不能像模板题那样,若查询结果为 \(p\)\(p<k\),则在 \([L,\text{mid}]\) 内查找剩下的 \(k-p\) 个数。但是若暴力往两个区间内插入 \(>\text{mid}\) 的数,肯定会 TLE。由于 \(>\text{mid}\) 的数在 \([L,\text{mid}]\) 仍有贡献(用它连接起两端的连续段,符合连续的限制),所以可以考虑先递归求解 \([L,\text{mid}]\) 的答案,再消除当前递归中的数在线段树中的贡献,再递归求解 \((\text{mid},R]\)。注意,在递归求解 \([L,\text{mid}]\) 的答案时,询问内的 \(k\)不能改成 \(k-p\),因为\(p\) 个值仍然在线段树上有贡献。这样就可以在保证效率的同时实现连续的限制了。

\(V\) 为值域(上界减下界),这样的方法时间复杂度和空间复杂度均为 \(\mathcal{O}((n+m)\cdot \log n\cdot \log V)\),可以接受。

\(\texttt{Code}\)

实现细节:

  • 我是用 vector 储存整体二分中的数与询问的。

  • 使用指令集或离散化(嫌麻烦没写 qwq)可以加快代码运行效率。

评测记录

$\texttt{Click for Code}$
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define ls x*2
#define rs x*2+1
const int N=100005;
int n,m,ans[N];
struct queries{
    int l,r,k,id;
};
vector<queries>Q;
struct num{
    int x,p;
};
vector<num>A;
struct tree{
    int len,llen,rlen,sz;
}sg[N*4];
void up(int x){
    sg[x].sz=sg[ls].sz+sg[rs].sz;
    sg[x].len=max({sg[ls].len,sg[rs].len,sg[ls].rlen+sg[rs].llen});
    sg[x].llen=(sg[ls].len==sg[ls].sz?sg[ls].len+sg[rs].llen:sg[ls].llen);
    sg[x].rlen=(sg[rs].len==sg[rs].sz?sg[rs].len+sg[ls].rlen:sg[rs].rlen);
}
void build(int x,int l,int r){
    if(l==r){
        sg[x]={0,0,0,1};
        return;
    }
    int mid=l+r>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    up(x);
}
void change(int x,int l,int r,int k,int v){
    if(l==r&&l==k){
        sg[x]={v,v,v,1};
        return;
    }
    int mid=l+r>>1;
    if(k<=mid){
        change(ls,l,mid,k,v);
    }else{
        change(rs,mid+1,r,k,v);
    }
    up(x);
}
tree query(int x,int l,int r,int ql,int qr){
    if(ql<=l&&qr>=r){
        return sg[x];
    }
    int mid=l+r>>1;
    if(qr<=mid){
        return query(ls,l,mid,ql,qr);
    }else if(ql>mid){
        return query(rs,mid+1,r,ql,qr);
    }else{
        tree t1=query(ls,l,mid,ql,qr),t2=query(rs,mid+1,r,ql,qr);
        return {max({t1.len,t2.len,t1.rlen+t2.llen}),(t1.len==t1.sz?t1.len+t2.llen:t1.llen),(t2.len==t2.sz?t2.len+t1.rlen:t2.rlen),t1.sz+t2.sz};
    }
}
void solve(vector<queries>q,vector<num>a,int l,int r){
    if(!q.size()||!a.size()){
        return;
    }
    if(l^r){
        int mid=l+r>>1;
        vector<queries>q1,q2;
        vector<num>a1,a2;
        for(auto i:a){
            if(i.x>mid){
                a1.push_back(i);
                change(1,1,n,i.p,1);
            }else{
                a2.push_back(i);
            }
        }
        for(auto i:q){
            tree tmp=query(1,1,n,i.l,i.r);
            if(tmp.len>=i.k){
                q1.push_back(i);
            }else{
                q2.push_back(i);
            }
        }
        solve(q2,a2,l,mid);
        for(auto i:a){
            if(i.x>mid){
                change(1,1,n,i.p,0);
            }
        }
        solve(q1,a1,mid+1,r);
    }else{
        for(auto i:q){
            ans[i.id]=l;
        }
    }
}
signed main(){
    cin>>n;
    for(int i=1,x;i<=n;++i){
        cin>>x;
        A.push_back({x,i});
    }
    cin>>m;
    for(int i=1,l,r,k;i<=m;++i){
        cin>>l>>r>>k;
        Q.push_back({l,r,k,i});
    }
    build(1,1,n);
    solve(Q,A,1,1e9);
    for(int i=1;i<=m;++i){
        cout<<ans[i]<<'\n';
    }
}
posted @ 2023-02-19 18:01  蒟蒻·廖子阳  阅读(32)  评论(0编辑  收藏  举报