第一场 2025 钉耙编程春季联赛 补题 1004

题目

给定一个长度为n ($n \le 1 \times 10^ 5 $ ) 的序列,q次查询,每次查询l, r.

求区间[l, r] 内满足奇数位最小值大于偶数位最大值(或者奇数位最大值小于偶数位最小值)的最长区间

思路

由于涉及区间操作,因此考虑线段树树状数组和st表那一套

对于满足条件的两种情况,可以任取一种处理,然后将每个元素取相反数再处理,因此只需要考虑一种情况

如果求l到r内最长距离,首先想到的是st表

\(f_{i,j}\)存的是i ~ i+ \(2^j-1\)段单点(作为右端点)最长路径的最大值

那么问题在于: 该最大值的左端点已经超出了l的范围

这个时候就会发现,

该问题和SP1684 FREQUENT - Frequent values(https://www.luogu.com.cn/problem/SP1684)非常相似

我们二分一个点使得该点x对应的左端点\(\le\)l,此时ans[i]的值为x - l;

然后对于从x到r这个区间内所有点,都不必顾及可能超出左端点的问题了,就可以直接用st表查询

接下来就是怎么记录每个点最远向左延申距离,使用双指针即可

在双指针时,需要判断任意两点之间是否满足条件,那么维护两个st表,分别记录区间最大值和最小值

总的来说,本题考查 双指针 + st表 + 一个巧妙的二分与st表操作的融合

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long int
const int INF = (1<<30)-1;
inline int read() {
    int ans = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans * f;
}
const int N = 1e5+10;
int a[N];
int n,q;
int ql[N], qr[N];
int ans[N];
int logn[N];
int pos[N];
int minval[22][N],maxval[22][N];
int len[22][N];
void rl() {
    n = read(), q= read();
    for (int i =1; i<= n; i++) {
        a[i] =read();
    }
    for (int i = 1;i<= q; i++) {
        ql[i] =read(),qr[i] =read();
    }
}
void init() {
    memset(ans,0,sizeof ans);
}
void initst() {
    logn[1] =0;
    logn[2] =1;
    for (int i =3; i< N; i++) {
        logn[i] = logn[i>>1] + 1;
    }
    for (int i =1; i<= n; i++) {
        if (i&1) {
            maxval[0][i] = a[i];
            minval[0][i] = INF;
        }
        else {
            maxval[0][i] = -INF;
            minval[0][i] = a[i];
        }
    }
    for (int i =1; i<= 21; i++) {
        for (int j =1; j + (1<<i)-1<= n; j++) {
            maxval[i][j] = max(maxval[i-1][j],maxval[i-1][j +( 1<<(i-1))]);
            minval[i][j] = min(minval[i-1][j],minval[i-1][j +( 1<<(i-1))]);
        }
    }

}
bool query(int l,int r) {
    int k = logn[r-l + 1];
    int ax = max(maxval[k][l],maxval[k][r- (1<<k)+1]);
    int in = min(minval[k][l],minval[k][r - (1<<k) + 1]);
    return ax < in;
}
int qy(int l,int r) {
    int k = logn[r-l+1];
    return max(len[k][l],len[k][r-(1<<k)+1]);
}
void calcu() {
    initst();
    for (int i = 1,j=1; i<=n; i++) {
        for (; j<=i && !query(j,i);j++);
        pos[i] = j;
        len[0][i] = i-j+ 1;
    }
    for (int i = 1; i<=21; i++) {
        for (int j = 1; j+ (1<<i)-1 <= n; j++) {
            len[i][j] = max(len[i-1][j],len[i-1][j + (1<<(i-1))]);
        }
    }
    for (int i =1; i<= q; i++) {
        int l = ql[i],r = qr[i];
        int x = (lower_bound(pos+l,pos+r+1,l) - pos);
        ans[i] = max(ans[i],x-l);
        if (x<=r) {
            ans[i] = max(ans[i],qy(x,r));
        }

    }
}
void rev() {
    for (int i =1; i<= n; i++) {
        a[i] = -a[i];
    }

}
const int mod = 1e9+7;
void out() {
    int res = 0;
    for (int i = 1; i<= q; i++) {
        res = (res + ans[i] * i)  % mod;
    }
    printf("%lld\n",res);
}
void solve() {
    rl();
    init();
    calcu();
    rev();
    calcu();
    out();
}

signed main() {
    int t = read();
    while (t--) {
        solve();
    }
    return 0;
}
posted @ 2025-03-13 12:50  Guaninf  阅读(19)  评论(0)    收藏  举报