题解 P7333 [JRKSJ R1] JFCA
一道不难的题目。
题意已经很明确了。我们发现数据范围是 $1\leq n\leq 10^5$,可以想到正解是一个 $\mathcal{O}(n\sqrt {n})$ 或 $\mathcal{O}(n \log n)$ 的做法。
题解
我们要找到与 $x$ 距离最近的 $i$,显然是按从 $x$ 递增和递减两个方向去比较。复杂度劣的主要原因是比较很多不满足条件的数,即 $a_i$ 的值比 $b_x$ 小。
为了优化复杂度,我们维护一个区间内 $a_i$ 最大值,如果区间最大值比 $b_x$ 小,那整个区间都不会有符合题意的 $a_i$,大大减少了比较次数。
但因为这是一个环,为了断环成链,需要开三倍空间,方便处理左右边界,并将原来的 $\left[1,n\right]$ 放到 $\left[n+1,n+n\right]$ 内处理,同时还有个小细节,即特判当 $f_x=n$ 时,这说明 $x$ 绕了 $n$ 个数回到了 $x$,此时将 $f_x \gets -1$。
容易想到分块。
对于 $x$ 所在的块内,直接暴力枚举块边。然后像原来一样,按距 $x$ 的距离递增比较每个块,若块内最大值大于 $b_x$,就继续按距 $x$ 的距离递增比较每个数,找到距 $x$ 最近的 $i$ 使 $a_i \geq b_x$。
时间复杂度为 $\mathcal{O}(n\sqrt{n})$,但因为对于随机期望运行次数很少,跑得飞快。
您也可以像出题人那样写二分 ST 表,思路都很简单。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n , a[N * 3] , b[N * 3] , belong[N * 3] , f[N];
int L , C , s[N * 3] , t[N * 3] , maxx[N * 3];
void init() {
C = 0;
L = sqrt(3 * n);
for(int i = 1; i <= n * 3; i += L) {
s[++ C] = i;
t[C] = min(n * 3 , i + L - 1);
for(int j = s[C]; j <= t[C]; j ++) {
belong[j] = C;
maxx[C] = max(maxx[C] , a[j]);
}
}
}
int query_l(int x , int k) {
int edge = belong[x];
for(int i = x - 1; i >= s[edge]; i --)
if(a[i] >= k)
return x - i;
for(int i = edge - 1; i >= 1; i --)
if(maxx[i] >= k)
for(int j = t[i]; j >= s[i]; j --)
if(a[j] >= k)
return x - j;
return -1;
}
int query_r(int x , int k) {
int edge = belong[x];
for(int i = x + 1; i <= t[edge]; i ++)
if(a[i] >= k)
return i - x;
for(int i = edge + 1; i <= C; i ++)
if(maxx[i] >= k)
for(int j = s[i]; j <= t[i]; j ++)
if(a[j] >= k)
return j - x;
return -1;
}
int main() {
scanf("%d" , &n);
for(int i = 1; i <= n; i ++)
scanf("%d" , &a[i]) , a[i + n] = a[i] , a[i + n + n] = a[i];
for(int i = 1; i <= n; i ++)
scanf("%d" , &b[i]);
init();
for(int i = 1; i <= n; i ++) {
f[i] = min(query_l(n + i , b[i]) , query_r(n + i , b[i]));
if(f[i] == n)
f[i] = -1;
}
for(int i = 1; i <= n; i ++)
printf("%d " , f[i]);
return 0;
}

浙公网安备 33010602011771号