第一场 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;
}

浙公网安备 33010602011771号