「CF484E」Sign on Fence「整体二分」「线段树」

题意

给定一个长度为\(n\)的正整数序列,第\(i\)个数为\(h_i\),\(m\)个询问,每次询问\((l, r, w)\),为\([l, r]\)所有长度为\(w\)的子区间最小值的最大值。(类似于一类特殊的直方图最大子矩形问题)

\(1 \leq n, m \leq 10^5\)

题解

我们考虑二分答案,这样\(n\)个数变成\(01\),若\(h_i\geq mid\)则为\(0\),否则为\(1\)

每次就相当于查询存不存在长度为\(w\)的连续\(1\)。用线段树维护。

这有个问题,\([l, r]\)分成\([l, mid - 1]\)和\([mid, r]\)的时候,左区间统计不到右区间的贡献。那我们就递归左区间之前不清空线段树,等到递归右区间的时候再清空。

时间复杂度两个log

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

const int N = 2e5 + 10;

struct opt { int l, r, k, id; } q[N], qL[N], qR[N];
int n, m, h[N], ans[N];
struct node { int res, l, r, len; } t[N << 2];

node operator + (const node &a, const node &b) {
    node ans; ans.len = a.len + b.len;
    ans.l = a.l == a.len ? a.l + b.l : a.l;
    ans.r = b.r == b.len ? b.r + a.r : b.r;
    ans.res = max(max(a.res, b.res), a.r + b.l);
    return ans;
}

void build(int u, int l, int r) {
    if(l == r) { t[u] = (node) {0, 0, 0, 1}; return ; }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    t[u] = t[u << 1] + t[u << 1 | 1];
}

void ins(int u, int l, int r, int x, int y) {
    if(l == r) { t[u] = (node) {y, y, y, 1}; return ; }
    int mid = (l + r) >> 1;
    if(x <= mid) ins(u << 1, l, mid, x, y);
    else ins(u << 1 | 1, mid + 1, r, x, y);
    t[u] = t[u << 1] + t[u << 1 | 1];
}

node qry(int u, int l, int r, int ql, int qr) {
    if(l == ql && r == qr) return t[u];
    int mid = (l + r) >> 1;
    if(qr <= mid) return qry(u << 1, l, mid, ql, qr);
    if(ql > mid) return qry(u << 1 | 1, mid + 1, r, ql, qr);
    return qry(u << 1, l, mid, ql, mid) + qry(u << 1 | 1, mid + 1, r, mid + 1, qr);
}

void solve(int ql, int qr, int l, int r) {
    if(ql > qr || l > r) return ;
    if(l == r) {
        for(int i = ql; i <= qr; i ++) ans[q[i].id] = l;
        return ;
    }
//  printf("[%d, %d] & [%d, %d]\n", ql, qr, l, r);
    int mid = (l + r + 1) >> 1, nl = 0, nr = 0;
    for(int i = ql; i <= qr; i ++) {
        if(!q[i].id) {
            if(q[i].k >= mid) ins(1, 1, n, q[i].l, 1), qR[nr ++] = q[i];
            else qL[nl ++] = q[i];
        } else {
            int res = qry(1, 1, n, q[i].l, q[i].r).res;
            if(res >= q[i].k) qR[nr ++] = q[i];
            else qL[nl ++] = q[i];
        }
    }
    for(int i = 0; i < nl; i ++) q[ql + i] = qL[i];
    for(int i = 0; i < nr; i ++) q[ql + nl + i] = qR[i];
    solve(ql, ql + nl - 1, l, mid - 1);
    for(int i = ql + nl; i <= qr; i ++) if(!q[i].id && q[i].k >= mid) ins(1, 1, n, q[i].l, 0);
    solve(ql + nl, qr, mid, r);
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) scanf("%d", h + i), q[i] = (opt) {i, 0, h[i], 0};
    int l = *min_element(h + 1, h + n + 1);
    int r = *max_element(h + 1, h + n + 1);
    scanf("%d", &m);
    for(int i = n + 1; i <= n + m; i ++) {
        scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].k); q[i].id = i - n;
    }
    build(1, 1, n); solve(1, n + m, l, r);
    for(int i = 1; i <= m; i ++) printf("%d\n", ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/hongzy/p/11378058.html

时间: 2024-10-02 06:25:14

「CF484E」Sign on Fence「整体二分」「线段树」的相关文章

(困难) 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

【XSY2720】区间第k小 整体二分 可持久化线段树

题目描述 给你你个序列,每次求区间第\(k\)小的数. 本题中,如果一个数在询问区间中出现了超过\(w\)次,那么就把这个数视为\(n\). 强制在线. \(n\leq 100000,a_i<n,w\leq n\) 题解 考虑整体二分. 先看看离线要怎么做. 现在我们要计算每个数对每个区间的贡献. 对于每个询问区间和每种数,让这个区间最右边\(w\)个数对这个询问的贡献为\(1\),第\(w+1\)个数对这个询问的贡献为\(-w\). 这样每个数的贡献就是二维平面上的一个矩形.可以用扫描线+线段

「线段树」[AHOI2009]维护序列

双倍经验,还是蓝题,岂不美哉 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为\(a_1,a_2,-,a_N\) .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入格式 第一行两个整数\(N\)和\(P\)\((1≤P≤1000000000)\). 第二行含有\(N\)个非负整数,从左到右依次为\(a_1,a_2,-,

loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

$ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinobu 早有准备,Alice.Ayaya.Karen.Shinobu.Yoko 五人又能继续愉快地玩耍啦! 「噢--!不是有放上天的烟花嘛!」Karen 兴奋地喊道. 「啊等等--」Yoko 惊呼.Karen 手持点燃引信的烟花,「嗯??」 Yoko 最希望见到的是排列优美的烟火,当然不会放过这个机会-

「模板」 线段树——区间乘 &amp;&amp; 区间加 &amp;&amp; 区间求和

「模板」 线段树--区间乘 && 区间加 && 区间求和 <题目链接> 原来的代码太恶心了,重贴一遍. #include <cstdio> int n,m; long long p; class SegmentTree { private: struct Node { int l,r; long long v,mul,add; Node *c[2]; Node(int l,int r):l(l),r(r),mul(1LL),add(0LL) { c[

docker并不能把部署的工作「减少为0」,比较好的情况下是「基本减少为1」

很多人说docker改变了运维世界,这句话是从群体角度来说的,是统计学意义上的改变,像mysql,python这样被大规模使用的基础应用,docker化之后为整个群体所节省的时间是非常巨大的. 有人可能会问,我只有一台服务器,也不太可能会迁移.我的python服务,mysql服务,只需要部署一次,就可以在以后重复使用了.那这样docker对于我来说还有优势吗?毕竟docker也是有学习成本的. 如果你确信你的应用都是一次性的,而且只提供给自己使用,那么docker在这种场景下的优势不是特别明显:

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

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

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

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

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

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