【题解】QOJ 894 Longest Loose Segment

QOJ 894 Longest Loose Segment

题意

给定一个长度为 \(n\) 的序列 \(a\),求出满足如下条件的区间的最长长度是多少:

  • \(\displaystyle \min_{i\in [l,r]} a_i+\max_{i\in [l,r]} a_i>r-l+1\)

\(m\) 组额外测试,每次在原数组的基础上交换若干元素的位置。

\(n\le 10^6,m\le 30\)

题解

知识点:笛卡尔树,动态规划。

被击杀了。

很显然这题卡对数做法,但是可以简要提一嘴。

使用最值分治,钦定最大值后,另外两项关于区间长度的变化具有显然的单调性,进而推出可二分性,做到 \(O(mn\log^2 n)\),其中一个 \(\log\) 常数很小。

其实也可以直接二分答案,只指定区间长度时(不指定具体位置),最大的 \(\max+\min\) 也是有单调性的,check 就只需要模拟一下了。

可惜模拟赛时没能想到这个简单做法,但无伤大雅,还是得到了这档分,不过没能感觉到这个单调性,我还是需要好好反思。

考虑线性做法,可以在笛卡尔树上 dp。

建出小根笛卡尔树,对于节点 \(u\) 计算 \(u\) 子树代表区间中的最长合法区间长度。

接下来按区间是否经过 \(u\) 进行分讨,对于不经过的情况,只需要从儿子转移过来就行了。

对于经过的情况,区间的 \(\min\) 就已经确定为了 \(a_u\)

发现这部分的计算不太好直接做,考虑找一下性质。

\(a_u\) 是区间的最小值,一个区间经过它会使得 \(\min+\max\) 更小,那么需要琢磨一下怎样的最优区间会经过 \(u\)

考虑一个拓展的过程(这里拿左子树举例),一个在左子树里的合法区间 \([l,r]\),通过拓展 \(k\) 个位置,使得右端点 \(\ge u\),稍加分析,如果这个过程满足最优性,那么右端点 \(\ge u\) 的必要条件就是左端点已经拓展到了最左端,因为左边不存在比 \(a_u\) 更小的数,先往左拓展一定是不劣的。

那么结论就很显然了,经过 \(u\) 的最优区间一定至少包含了 \(u\) 的左右子树之一。

那么 \(\max\) 可以取子树的 \(\max\),依据 \(\max+\min>len\),得到 \(\max+\min-1\ge len\),可以计算得出合法区间长度的上界,显然长度越长越好,但是要和 \(u\) 子树的实际区间长度取 \(\min\),且这个上界要比其儿子子树区间长度长。

更多没有提及的细节可以阅读代码。

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define sz(x) (int)(x).size()

#define N 1002511
// #define int long long

int n,m,a[N],l[N],r[N];
int st[N],tp,rt;
int f[N],len[N],mx[N];

inline void buildl(){
    tp=0;
    rep(i,1,n){
        int u=0;
        while(tp&&a[st[tp]]>a[i]){
            u=st[tp];
            tp--;
        }
        l[i]=u;
        st[++tp]=i;
    }
}

inline void buildr(){
    tp=0;
    per(i,1,n){
        int u=0;
        while(tp&&a[st[tp]]>=a[i]){
            u=st[tp];
            tp--;
        }
        r[i]=u;
        st[++tp]=i;
    }
}

inline void dfs(int k){
    int v=a[k];
    len[k]=1;
    mx[k]=v;
    f[k]=(2*a[k])>1;

    if(l[k]){
        dfs(l[k]);
        len[k]+=len[l[k]];
        mx[k]=max(mx[k],mx[l[k]]);
        f[k]=max(f[k],f[l[k]]);

        int re=mx[l[k]]+v-1;
        if(re>len[l[k]]){
            f[k]=max(f[k],re);
        }
    }
    if(r[k]){
        dfs(r[k]);
        len[k]+=len[r[k]];
        mx[k]=max(mx[k],mx[r[k]]);
        f[k]=max(f[k],f[r[k]]);

        int re=mx[r[k]]+v-1;
        if(re>len[r[k]]){
            f[k]=max(f[k],re);
        }
    }

    f[k]=min(f[k],len[k]);
}

inline int sol(){
    buildl();
    buildr();

    rt=1;
    rep(i,1,n){
        if(a[i]<a[rt]){
            rt=i;
        }
    }

    dfs(rt);

    return f[rt];
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    cin>>n>>m;

    rep(i,1,n){
        cin>>a[i];
    }

    cout<<sol()<<'\n';

    rep(i,1,m){
        int c;
        cin>>c;

        rep(j,1,c){
            int x,y;
            cin>>x>>y;
            swap(a[x],a[y]);
        }

        cout<<sol()<<'\n';
    }

    return 0;
}
posted @ 2025-11-06 17:04  Lucyna_Kushinada  阅读(4)  评论(0)    收藏  举报