Codeforces 484E Sign on Fence(是持久的段树+二分法)

题目链接:Codeforces 484E Sign on Fence

题目大意:给定给一个序列,每一个位置有一个值,表示高度,如今有若干查询,每次查询l,r,w,表示在区间l,r中,

连续最长长度大于w的最大高度为多少。

解题思路:可持久化线段树维护区间合并,前端时间碰到一题可持久化字典树,就去查了一下相关论文,大概知道了是

什么东西。

将高度依照从大到小的顺序排序,然后每次插入一个位置,线段树维护最长连续区间,由于插入是依照从大到小的顺

序,所以每次的线段树中的连续最大长度都是满足高度大于等于当前新插入的height值。对于每次查询,二分高度,因

为高度肯定是在已有的高度中,所以仅仅接二分下表就可以。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

const int maxn = 1e6 + 5;
typedef pair<int,int> pii;
struct Node {
    int lc, rc, lp, rp, L, R, S;
    int length() {
        return rp - lp + 1;
    }
}nd[maxn << 2];

int N, sz, root[maxn];
pii blo[maxn];

inline int newNode() {
    return sz++;
}

inline void pushup(int u) {
    int lcid = nd[u].lc, rcid = nd[u].rc;
    nd[u].L = nd[lcid].L + (nd[lcid].L == nd[lcid].length() ? nd[rcid].L : 0);
    nd[u].R = nd[rcid].R + (nd[rcid].R == nd[rcid].length() ? nd[lcid].R : 0);
    nd[u].S = max(nd[lcid].R + nd[rcid].L, max(nd[lcid].S, nd[rcid].S));
}

inline Node merge(Node a, Node b) {
    Node u;
    u.lp = a.lp; u.rp = b.rp;
    u.L = a.L + (a.L == a.length() ? b.L : 0);
    u.R = b.R + (b.R == b.length() ? a.R : 0);
    u.S = max(a.R + b.L, max(a.S, b.S));
    return u;
}

void build(int& u, int l, int r) {
    if (u == 0) u = newNode();
    nd[u] = (Node){0, 0, l, r, 0, 0, 0};

    if (l == r)
        return;
    int mid = (l + r) >> 1;
    build(nd[u].lc, l, mid);
    build(nd[u].rc, mid +1, r);
    pushup(u);
}

int insert(int u, int x) {
    int k = newNode();
    nd[k] = nd[u];

    if (nd[k].lp == x && x == nd[k].rp) {
        nd[k].S = nd[k].L = nd[k].R = 1;
        return k;
    }

    int mid = (nd[k].lp + nd[k].rp) >> 1;
    if (x <= mid)
        nd[k].lc = insert(nd[k].lc, x);
    else
        nd[k].rc = insert(nd[k].rc, x);
    pushup(k);
    return k;
}

Node query(int u, int l, int r) {
    if (l <= nd[u].lp && nd[u].rp <= r)
        return nd[u];

    int mid = (nd[u].lp + nd[u].rp) >> 1;
    if (r <= mid)
        return query(nd[u].lc, l, r);
    else if (l > mid)
        return query(nd[u].rc, l, r);
    else {
        Node ll = query(nd[u].lc, l, r);
        Node rr = query(nd[u].rc, l, r);
        return merge(ll, rr);
    }
}

inline bool cmp (const pii& a, const pii& b) {
    return a.first > b.first;
}

void init () {
    sz = 1;
    scanf("%d", &N);

    for (int i = 1; i <= N; i++) {
        scanf("%d", &blo[i].first);
        blo[i].second = i;
    }
    sort(blo + 1, blo + 1 + N, cmp);
    build(root[0], 1, N);
    for (int i = 1; i <= N; i++)
        root[i] = insert(root[i-1], blo[i].second);
}

int main () {
    init();

    int q, l, r, w;
    scanf("%d", &q);
    while (q--) {
        scanf("%d%d%d", &l, &r, &w);
        int L = 1, R = N;
        while (L < R) {
            int mid = (L + R) >> 1;
            if (query(root[mid], l, r).S >= w)
                R = mid;
            else
                L = mid + 1;
        }
        printf("%d\n", blo[L].first);
    }
    return 0;
}
时间: 2024-10-10 12:23:39

Codeforces 484E Sign on Fence(是持久的段树+二分法)的相关文章

Codeforces 484E Sign on Fence(可持久化线段树+二分)

题目链接:Codeforces 484E Sign on Fence 题目大意:给定给一个序列,每个位置有一个值,表示高度,现在有若干查询,每次查询l,r,w,表示在区间l,r中, 连续最长长度大于w的最大高度为多少. 解题思路:可持久化线段树维护区间合并,前端时间碰到一题可持久化字典树,就去查了一下相关论文,大概知道了是 什么东西. 将高度按照从大到小的顺序排序,然后每次插入一个位置,线段树维护最长连续区间,因为插入是按照从大到小的顺 序,所以每次的线段树中的连续最大长度都是满足高度大于等于当

Codeforces 484E. Sign on Fence 可持久化线段树

大概题意: 给一数组a,问在某一区间L~R中,问对于连续的长为W的一段中最小的数字的最大值是多少. 显然可以转化成二分高度然后判断可行性的问题. 考虑到高度肯定为数组中的某一个值,将数组从大到小排序. 建n棵线段树,对于第 i 棵线段树,将 大于等于a[i] 的叶子的值设置为1,其他的叶子设置为0,问题就转化成了用线段树求某一区间中最长的连续的1的个数,这是一个线段树的经典问题,可以通过维护每个节点的 左边连续和,右边连续和,连续和的最大值 得到. 由于空间问题,不可能建立10^5棵线段树,考虑

CodeForces 484E Sign on Fence

题意: n(10^5)个模板形成的栅栏  q(10^5)个询问  每个询问要求在[u,v]木板区间内摆放一个宽度为w的矩形  问矩形最大的高是多少 思路: 对于每个询问  可以通过logn的二分来将求解最大h的问题转化为当前h'情况下的判定问题 为什么可以二分呢  因为如果我们将木板排序  从大到小的依次放置它们的位置上  那么对于某一时刻  线段上连续的1就代表了矩形的宽  同时这时的h最小为二分到的木板高度h'  明显h'和这时矩形的宽满足单调性 得到了h'后只需要快速的找出[u,v]区间连

(困难) CF 484E Sign on Fence,整体二分+线段树

Bizon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i-th panel's height is hi meters. The adjacent planks follow without a gap between them. Afte

BZOJ 2588 Count on a tree (COT) 是持久的段树

标题效果:两棵树之间的首次查询k大点的权利. 思维:树木覆盖树,事实上,它是正常的树木覆盖了持久段树. 由于使用权值段树可以寻求区间k大,然后应用到持久段树思想,间隔可以做减法.详见代码. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define NIL (tree[0]) using names

codeforces 349B Color the Fence 贪心,思维

1.codeforces 349B    Color the Fence 2.链接:http://codeforces.com/problemset/problem/349/B 3.总结: 刷栅栏.1-9每个字母分别要ai升油漆,问最多可画多大的数字. 贪心,也有点考思维. #include<bits/stdc++.h> using namespace std; #define LL long long #define INF 0x3f3f3f3f int main() { int v,a[1

Codeforces 706D Vasiliy&#39;s Multiset(可持久化字典树)

[题目链接] http://codeforces.com/problemset/problem/706/D [题目大意] 要求实现一个集合中的三个操作,1:在集合中加入一个元素x,2:从集合中删除一个元素x(保证x存在),3:要求从集合中选出一个数,使得其与给出的数x的异或值最大,输出这个异或值. [题解] 可以将所有的以二进制形式存在01字典树上,删除即插入权值为-1的二进制串,对于异或值最大的操作,我们只要在字典树上按位贪心,从最高位开始尽量保证该位存在最后就能得到答案.写代码的时候直接写了

Codeforces 458C Elections 贿赂选票抢主席! 线段树

题目链接:点击打开链接 题意: 给定n张选票,每张选票有2个参数,第一个参数表示这张选票选的人 第二个参数表示如果让这张选票改为选0号 的花费 问:使得0号的选票是最高的(不能有和0号相同)的最小花费 枚举0号的最终选票 那么已知0号最终选票,则有些人选票比0号大的,那些票都要买下来. 如果买完了还是达不到 最终选票,就从所有剩下的选票里找前k小的. 用线段树求前k小的数的和,然后_(:зゝ∠)_就可以了 #include<iostream> #include<cstdio> #i

Codeforces 1136E Nastya Hasn&#39;t Written a Legend (线段树教做人系列)

题意:有一个数组a和一个数组k,数组a一直保持一个性质:a[i + 1] >= a[i] + k[i].有两种操作:1,给某个元素加上x,但是加上之后要保持数组a的性质.比如a[i]加上x之后,a[i + 1]<a[i] + k[i],那么a[i + 1]就变成a[i] + k[i],否则不变.同理,若a[i + 2]小于了现在的a[i + 1] + k[i + 1],那么a[i + 2]也变成a[i + 1] + k[i + 1],一直保持这个性质.第二章操作,询问数组a的区间[l, r]的