【2019牛客暑期多校训练营 第一场 A题 Equivalent Prefixes】【单调栈】【RMQ+二分】

题目链接:
https://ac.nowcoder.com/acm/contest/881/A
题意:
给你a,b两个数列,求最大的 p p p满足,任意的 1 ≤ l ≤ r ≤ p 1\leq l\leq r\leq p 1lrp有, 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 p1是满足条件的,又因为新增的数只有一个,所以新增的区间也就是 [ j , p ] , j ∈ [ 1 , p − 1 ] [j, p],j∈[1,p-1] [j,p]j[1,p1]
那就只需要判断数列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(p1,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值