题目链接:
https://ac.nowcoder.com/acm/contest/881/A
题意:
给你a,b两个数列,求最大的
p
p
p满足,任意的
1
≤
l
≤
r
≤
p
1\leq l\leq r\leq p
1≤l≤r≤p有,
R
M
Q
(
a
,
l
,
r
)
=
R
M
Q
(
b
,
l
,
r
)
RMQ(a, l, r) = RMQ(b,l,r)
RMQ(a,l,r)=RMQ(b,l,r),注意此处RMQ不是区间最小值的意思,而是区间最小值所在的位置。
题解:
这里想法很简单,就是
p
p
p每次加一,然后判断满不满足条件。
假设当前检查到
p
p
p,那么
p
−
1
p-1
p−1是满足条件的,又因为新增的数只有一个,所以新增的区间也就是
[
j
,
p
]
,
j
∈
[
1
,
p
−
1
]
[j, p],j∈[1,p-1]
[j,p],j∈[1,p−1]。
那就只需要判断数列a和b对应新增区间的RMQ(最小值位置)是否相同。
以数列a为例,对于区间
[
j
,
p
]
[j,p]
[j,p],只是新增了一个数
a
[
p
]
a[p]
a[p],如果
a
[
p
]
a[p]
a[p]大于区间最小值那就不会更新,反之则更新。
而这里需要数列a和b对应区间的RMQ相同,也就是说,要么他们的RMQ同时更新,或者都不更新,因此我们找到他们RMQ更新的位置,判断两个位置是否相同即可。
方法1是构造一个单调递增的栈,这样新加入的元素在栈中的位置就RMQ更新的次序,所以如果新加入的元素在栈中的位置相同就能说明RMQ是否同步更新,而都没入栈则说明都没更新。
方法2是二分+RMQ,以下的RMQ意为区间最小值,很明显,
R
M
Q
(
1
,
p
)
≤
R
M
Q
(
2
,
p
)
≤
⋅
⋅
⋅
≤
R
M
Q
(
p
−
1
,
p
)
RMQ(1,p) \leq RMQ(2,p) \leq ··· \leq RMQ(p - 1, p)
RMQ(1,p)≤RMQ(2,p)≤⋅⋅⋅≤RMQ(p−1,p),因为这些区间是有包含关系的,随着区间的减小,元素数量减少,可能就丢失了原来的最小值,使得区间最小值变大,因为他们的递增关系,所以你可以二分位置查询最后一个小于等于新加入元素的位置,具体可见代码。
法1代码:
const int MAX = 1e5 + 10;
int N, top1, top2;
int a[MAX], b[MAX], st1[MAX], st2[MAX];
int main() {
register int i;
while (~scanf("%d", &N)) {
for (i = 1; i <= N; i++)scanf("%d", a + i);
for (i = 1; i <= N; i++)scanf("%d", b + i);
top1 = top2 = 0;
st1[0] = st2[0] = -1;
for (i = 1; i <= N; i++) {
while (a[i] <= st1[top1])top1--;
st1[++top1] = a[i];
while (b[i] <= st2[top2])top2--;
st2[++top2] = b[i];
if (top1 != top2)break;
}
printf("%d\n", i - 1);
}
return 0;
}
法2代码:
const int MAX = 1e5 + 10;
int dpa[MAX][30];
int dpb[MAX][30];
int T, n;
void ST() {
for (int i = 1; i <= n; i++)
dpa[i][0] = read();
for (int i = 1; i <= n; i++)
dpb[i][0] = read();
for (int j = 1; j < 20; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
dpa[i][j] = min(dpa[i][j - 1], dpa[i + (1 << (j - 1))][j - 1]);
dpb[i][j] = min(dpb[i][j - 1], dpb[i + (1 << (j - 1))][j - 1]);
}
}
int RMQ1(int l, int r) {//数列a查询最小值
if (l > r)return 0;
int k = floor(log2(r - l + 1));
return min(dpa[l][k], dpa[r - (1 << k) + 1][k]);
}
int RMQ2(int l, int r) {//数列b查询最小值
if (l > r)return 0;
int k = floor(log2(r - l + 1));
return min(dpb[l][k], dpb[r - (1 << k) + 1][k]);
}
bool judge(int p, int target1, int target2) {//二分查询最后一个小于等于新加入元素的位置
int pos1 = 0, pos2 = 0;
int l = 1, r = p - 1, m;
while (l <= r) {
int m = (l + r) / 2;
if (RMQ1(m, p - 1) <= target1)pos1 = m, l = m + 1;
else r = m - 1;
}
l = 1, r = p - 1;
while (l <= r) {
int m = (l + r) / 2;
if (RMQ2(m, p - 1) <= target2)pos2 = m, l = m + 1;
else r = m - 1;
}
if (pos1 == pos2)return 1;
else return 0;
}
int main() {
while (scanf("%d", &n) != EOF) {
ST();
int p = 0;
for (int i = 1; i <= n; i++) {
if (judge(i, dpa[i][0], dpb[i][0]))p++;
else break;
}
printf("%d\n", p);
}
return 0;
}