可持久化线段树 CF484E-Sign on Fence

题目链接

题目大意:1-n每个位置上有一个固定高度的建筑物,有若干次询问,每次询问 [l, r] 区间内,连续横跨长度为w,高度最矮的建筑物最高为多少。

解题思路:可持久化线段树,按高度为建树,每次询问为针对一段区间,最长的连续和为多少。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <cassert>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#define  RD(x)      scanf("%d", &x)
#define  REP(i, n)  for (int i=0; i<int(n); i++)
#define  FOR(i, n)  for (int i=1; i<=int(n); i++)
#define  pii        pair<int, int>
#define  mp         make_pair
#define  pb         push_back
inline   int read(){int ret; scanf("%d", &ret); return ret;}
//int dx[8] = {1, 1, 0, -1, -1, -1, 0, 1};
//int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};

using namespace std;
#define  N   123456
#define  M   22222
#define  eps 1e-8
#define  pi  acos(-1.0)
#define  inf 0X5FFFFFFFFFFFFFFFll
#define  mod 1000000007ll
#define  LL  long long
#define  ULL unsigned long long
struct Seg {
	Seg *l, *r;
	int ll, rr, len, d;
	void pushUp() {
		len = max((l?l->rr:0) + (r?r->ll:0),
				max(l?l->len:0, r?r->len:0));
		ll = l?l->ll:0;
		if (l && r && l->len == l->d)
			ll = l->d + r->ll;
		rr = r?r->rr:0;
		if (l && r && r->len == r->d)
			rr = r->d + l->rr;
	}
}pool[N*20], *cur = pool;

Seg *root[N];

Seg *newNode(int l, int r) {
	cur->l = cur->r = NULL;
	cur->ll = cur->rr = cur->len = 0;
	cur->d = r - l + 1;
	return cur++;
}

Seg *update(int x, int l, int r, Seg *now) {
	Seg *ret = newNode(l, r);
	if (now != NULL)
		*ret = *now;
	if (l == r) {
		ret->ll = ret->rr = ret->len = 1;
		return ret;
	}
	int mid = (l + r) >> 1;
	if (x <= mid)
		ret->l = update(x, l, mid, now?now->l:NULL);
	if (x > mid)
		ret->r = update(x, mid+1, r, now?now->r:NULL);
	ret->pushUp();
	return ret;
}

Seg *p0[N];
int tot;
void _query(int l, int r, int L, int R, Seg *now) {
	if (l <= L && R <= r) {
		p0[tot++] = now;
		return;
	}
	int mid = (L + R) >> 1;
	if (l <= mid)
		_query(l, r, L, mid, now?now->l:NULL);
	if (r > mid)
		_query(l, r, mid+1, R, now?now->r:NULL);
}

int query(int l, int r, int L, int R, Seg *now) {
	tot = 0;
	_query(l, r, L, R, now);
	int pre = 0;
	int ret = 0;
	REP(i, tot) {
		pre += p0[i]?p0[i]->ll:-pre;
		ret = max(ret, max(pre, p0[i]?p0[i]->len:0));
		pre = p0[i]?(p0[i]->len == p0[i]->d? pre:p0[i]->rr):0;
	}
	return ret;
}

pii h[N];
int height[N];

int Main() {
	//freopen("cf.txt", "r", stdin);
	ios_base::sync_with_stdio(0);
	int n, m;
	cin >> n;
	FOR(i, n) {
		cin >> h[i].first;
		h[i].second = i;
	}
	sort(h + 1, h + n + 1, [](const pii &a, const pii &b) {return a.first > b.first;});

	FOR(i, n)
		root[i] = update(h[i].second, 1, n, root[i-1]);

	cin >> m;
	while (m--) {
		int L, R, W;
		cin >> L >> R >> W;
		int l = 1, r = n;
		while (l < r) {
			int mid = (l + r) >> 1;
			if (query(L, R, 1, n, root[mid]) >= W)
				r = mid;
			else
				l = mid + 1;
		}
		cout << h[l].first << endl;
	}

	return 0;
}


int main() {
	return Main();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值