Codeforces #276 div.1 E

题目链接:http://codeforces.com/contest/484/problem/E


题目大意:给定一个长度为n的序列,以及Q个询问 (l, r, w) , 每个询问输出 max{ min(a[i], a[i + 1], ..., a[i + w - 1]),  i >= l, i + w - 1 <= r}


基本思路:考虑数字从大到小加入序列,对于第i个询问(li,ri,wi)。如果答案ans是合法的,则加到数字ans时,区间[li, ri]中必然有连续一段长度大于等于w的数字已经被加入。如果我们只需要验证询问的答案是否都大于等于ans,则我们可以把大于等于ans的数全部加入序列,然后用线段树就可以很好地处理。

对于原问题,我们只需要二分答案,然后利用可持久化线段树即可。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1e5 + 5, M = 8e6 + 5;
pair<int, int> a[N];
int ll[M], rr[M], root[N];
int leftMax[M], rightMax[M], allMax[M];
int tot;

int cmp(const pair<int, int> &a, const pair<int, int> &b) {
    return a > b;
}

void newpoint(int pre, int &x) {
    x = ++tot;
    ll[x] = ll[pre];
    rr[x] = rr[pre];
    leftMax[x] = leftMax[pre];
    rightMax[x] = rightMax[pre];
    allMax[x] = allMax[pre];
}

void add(int prev, int &k, int m, int n, int c) {
    newpoint(prev, k);
    if (m > c || n < c) return ;
    if (m == n) {
	leftMax[k] = rightMax[k] = allMax[k] = 1;
	return ;
    }
    int mid = m + n >> 1;
    int &z1 = ll[k], &z2 = rr[k]; 
    add(ll[prev], z1, m, mid, c);
    add(rr[prev], z2, mid + 1, n, c);
    if (leftMax[z1] == mid - m + 1) leftMax[k] = leftMax[z1] + leftMax[z2];
    else leftMax[k] = leftMax[z1];
    if (rightMax[z2] == n - mid) rightMax[k] = rightMax[z2] + rightMax[z1];
    else rightMax[k] = rightMax[z2];
    allMax[k] = max(allMax[z1], allMax[z2]);
    allMax[k] = max(allMax[k], rightMax[z1] + leftMax[z2]);
}

int find(int k, int m, int n, int l, int r, int &L) {
    if (m > r || n < l || k == 0) return 0;
    if (m >= l && n <= r) {
	int t = max(L + leftMax[k], allMax[k]);
	if (allMax[k] == n - m + 1) L += n - m + 1;
	else L = rightMax[k];  
	return t;
    }
    int mid = m + n >> 1;
    int t1 = find(ll[k], m, mid, l, r, L);
    int t2 = find(rr[k], mid + 1, n, l, r, L);
    return max(t1, t2);
}

int main() {
    // freopen("1.txt", "r", stdin);
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
	scanf("%d", &a[i].first);
	a[i].second = i;
    }
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1; i <= n; ++i) {
	int c = a[i].second;
	add(root[i - 1], root[i], 1, n, c);
    }
    int Q;
    scanf("%d", &Q);
    while (Q--) {
	int l, r, w;
	scanf("%d%d%d", &l, &r, &w);
	int head = 1, tail = n;
	int L;
	while (head + 1 < tail) {
	    int mid = head + tail >> 1;
	    L = 0;
	    if (find(root[mid], 1, n, l, r, L) >= w)
		tail = mid;
	    else head = mid;
	}
	L = 0;
	if (find(root[head], 1, n, l, r, L) >= w)
	    tail = head;
	L = 0;
	printf("%d\n", a[tail].first);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值