【分治】JZOJ_6308 中间值

题意

给出两个非严格单调递增序列,有修改和询问操作,对于询问操作,求出两个序列中取出两段组成新一段的第 k k k大的数。

思路

假设:X序列为X[xBeg…xEnd],而Y序列为Y[yBeg…yEnd]。

将序列X和Y都均分2段,即取X序列中间位置为xMid (xMid = xBeg+(xEnd-xBeg)/2),也同理取序列Y中间位置为yMid。
比较X[xMid]和Y[yMid]的大小,此时记录X左段和Y左段元素个数合计为halfLen,即halfLen = xMid-xBeg+yMid-yBeg+2。

  1. 当X[xMid] < Y[yMid]时,在合并的数组中,原X[xBeg…xMid]所有元素一定在Y[yMid]的左侧,
    (1) 若k < halfLen,则此时第k大的元素一定不会大于Y[yMid]这个元素,故以后没有必要搜索 Y[yMid…yEnd]这些元素,可弃Y后半段数据。
    此时只需递归的对X序列+Y序列的前半段,去搜索第k小的数。

    (2) 若k >= halfLen,则此时第k大的元素一定不会小于X[xMid]这个元素,故以后没有必要搜索 X[xBeg…xMid]这些元素,可弃X前半段数据。
    此时只需递归的对X序列的后半段+Y序列,去搜索第 k-(xMid-xBeg+1)小的数。

  2. 当X[xMid] >= Y[yMid]时,在合并的数组中,原Y[yBeg…yMid]的所有元素一定在X[xMid]的左侧,
    (1) 若k < halfLen,则此时第k大的元素一定不会大于X[xMid]这个元素,故以后没有必要搜索 X[xMid…xEnd]这些元素,可弃X后半段数据。
    此时只需递归的对X序列的前半段+Y序列,去搜索第k小的数。

    (2) 若k >= halfLen,则此时第k大的元素一定不会小于Y[yMid]这个元素,故以后没有必要搜索 Y[yBeg…yMid]这些元素,可弃Y前半段数据。
    此时只需递归的对X序列+Y序列的后半段,去搜索第 k-(yMid-yBeg+1)小的数。

以上转自blog

简单说,就是分治,之后会得出 4 4 4种情况,每种情况都能减去一半的序列。

代码

#include <cctype>
#include <cstdio>
#include <algorithm>
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)

int n, m;
int a[500001], b[500001], c[500001];
char buf[1 << 21], *p1 = buf, *p2 = buf;

inline int read() {
    int res = 0, f = 0;
	char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') f = 1;
        ch = getchar();
    }
    while (isdigit (ch)) {
        res = res * 10 + ch - 48;
        ch = getchar();
    }
    return f ? -res : res;
}

int find(int aLeft, int aRight, int bLeft, int bRight, int k) {
	if (aLeft > aRight) return b[bLeft + k - 1];
	if (bLeft > bRight) return a[aLeft + k - 1];
	int aMid = aRight + aLeft + 1 >> 1, bMid = bRight + bLeft + 1 >> 1;
	int halfLen = aMid - aLeft + 1 + bMid - bLeft + 1;
	if (a[aMid] < b[bMid]) {
		if (k < halfLen) return find(aLeft, aRight, bLeft, bMid - 1, k);
		else return find(aMid + 1, aRight, bLeft, bRight, k - (aMid - aLeft + 1));
	} else {
		if (k < halfLen) return find(aLeft, aMid - 1, bLeft, bRight, k);
		else return find(aLeft, aRight, bMid + 1, bRight, k - (bMid - bLeft + 1));
	}
}

int main() {
	file(median);
	n = read();
	m = read();
	for (register int i = 1; i <= n; i++)
		a[i] = read();
	for (register int i = 1; i <= n; i++)
		b[i] = read();
	for (register int op; m; m--) {
		op = read();
		if (op == 1) {
			int x, y, z;
			x = read();
			y = read();
			z = read();
			if (x) b[y] = z;
			else a[y] = z;
		} else {
			int l1, l2, r1, r2;
			l1 = read();
			r1 = read();
			l2 = read();
			r2 = read();
			printf("%d\n", find(l1, r1, l2, r2, (r1 - l1 + 1 + r2 - l2 + 1) / 2 + 1));
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值