[CF484E]Sign on Fence

题目大意:一个长度为$n$的数列,$m$次询问。每次询问$l\;r\;k$,表示在区间$[l,r]$内选一个长度为$k$的区间,求区间最小数的最大值

题解:常见操作,开一棵主席树,比这一位大的就赋成$1$,否则为$0$,维护前缀$1$的个数,后缀$1$的个数和区间最长$1$的个数,二分答案判断一下就行了

卡点:

 

C++ Code:

#include <cstdio>
#include <algorithm>
#define maxn 100010
#define N maxn * 20
int n, m;
int v[maxn], rnk[maxn];
inline int min(int a, int b) {return a < b ? a : b;}
inline int max(int a, int b) {return a > b ? a : b;}
inline bool cmp(int a, int b) {return v[a] > v[b];}
inline bool cmp1(int a, int b) {return a > b;}
namespace Segment_Tree {
	struct node {
		int ml, mr, len, sz;
		node() {ml = mr = len = sz = 0;}
		node(int x) {ml = mr = len = sz = x;}
		inline node operator + (node rhs) const {
			node res;
			res.len = max(len, rhs.len);
			res.ml = ml + (sz == ml ? rhs.ml : 0);
			res.mr = rhs.mr + (rhs.sz == rhs.mr ? mr : 0);
			res.len = max(res.len, mr + rhs.ml);
			res.sz = sz + rhs.sz;
			return res;
		}
	} V[N];
	int root[maxn], lc[N], rc[N], idx;
	void build(int &rt, int l, int r) {
		rt = ++idx;
		if (l == r) {
			V[rt].sz = 1;
			return ;
		}
		int mid = l + r >> 1;
		build(lc[rt], l, mid);
		build(rc[rt], mid + 1, r);
		V[rt] = V[lc[rt]] + V[rc[rt]];
	}
	int p;
	void ADD(int &rt, int l, int r) {
		lc[++idx] = lc[rt], rc[idx] = rc[rt], V[idx] = V[rt]; rt = idx;
		if (l == r) {
			V[rt] = node(1);
			return ;
		}
		int mid = l + r >> 1;
		if (p <= mid) ADD(lc[rt], l, mid);
		else ADD(rc[rt], mid + 1, r);
		V[rt] = V[lc[rt]] + V[rc[rt]];
	}
	inline void add(int &rt, int P) {
		p = P;
		ADD(rt, 1, n);
	}
	int L, R;
	node ASK(int rt, int l, int r) {
		if (L <= l && R >= r) return V[rt];
		int mid = l + r >> 1;
		node ans;
		if (L <= mid) ans = ASK(lc[rt], l, mid);
		if (R > mid) ans = ans + ASK(rc[rt], mid + 1, r);
		return ans;
	}
	inline node ask(int rt, int ll, int rr) {
		L = ll, R = rr;
		return ASK(rt, 1, n);
	}
}
using Segment_Tree::add;
using Segment_Tree::ask;
using Segment_Tree::root;
int LL, RR, KK;
inline bool check(int mid) {
	return ask(root[mid], LL, RR).len >= KK;
}
inline int solve() {
	int l = 1, r = n, ans = n;
	while (l <= r) {
		int mid = l + r >> 1;
		if (check(mid)) {
			ans = mid;
			r = mid - 1;
		} else l = mid + 1; 
	}
	return v[ans];
}
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", v + i), rnk[i] = i;
	std::sort(rnk + 1, rnk + n + 1, cmp);
	std::sort(v + 1, v + n + 1, cmp1);
	Segment_Tree::build(root[0], 1, n);
	for (int i = 1; i <= n; i++) {
		root[i] = root[i - 1];
		add(root[i], rnk[i]);
	}
	scanf("%d", &m);
	while (m --> 0) {
		scanf("%d%d%d", &LL, &RR, &KK);
		printf("%d\n", solve());
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/Memory-of-winter/p/9743205.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值