可持续化线段树(例题Sign on Fence[Codeforces 484E])

刚刚学习的想记录一下:

第一次接触可持续化线段树,很懵。。。

题目:

题目描述

izon 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.

After Bizon painted the fence he decided to put a "for sale" sign on it.The sign will be drawn on a rectangular

piece of paper and placed on the fence so that the sides of the sign are parallel to the fence panels and are

also aligned with the edges of some panels. Bizon the Champion introduced the following constraints for the sign position:

1 The width of the sign should be exactly w meters.

2 The sign must fit into the segment of the fence from the l-th to the r-th panels, inclusive

(also, it can‘t exceed the fence‘s bound in vertical direction).

The sign will be really pretty, So Bizon the Champion wants the sign‘s height to be as large as possible.

You are given the description of the fence and several queries for placing sign. For each query print the maximum

possible height of the sign that can be placed on the corresponding segment of the fence with the given fixed width of the sign.

输入

The first line of the input contains integer n — the number of panels in the fence (1?≤?n?≤?105).

The second line contains n space-separated integers hi, — the heights of the panels (1?≤?hi?≤?109).

The third line contains an integer m — the number of the queries (1?≤?m?≤?105).

The next m lines contain the descriptions of the queries,

each query is represented by three integers lr and w (1?≤?l?≤?r?≤?n,1?≤?w?≤?r?-?l?+?1)

— the segment of the fence and the width of the sign respectively.

输出

For each query print the answer on a separate line — the maximum height of the sign that can be put in the corresponding segment of the fence with all the conditions being satisfied.

样例输入

5 1 2 2 3 3 3 2 5 3 2 5 2 1 5 5

样例输出

2 3 1

好吧大意就是给我们一组数,并给出m个询问,每个询问包括l,r,w三个数,询问我们在l到r这个区间内连续取W个数,使这w个数中的最小值尽可能的大,输出这个最大的最小值。

首先我们先假设在当前的询问下X这个数可以成为连续w个数中的最小值(x一定为某个数的值),然后把数列中大于X的数标为1小于X的数标为0,那么如果我们求出这个01串中最长的1串的长度为s,且s>=w的话,x就可以拿去更新当前答案,然后我们就枚举比x大的值看是否符合条件,当然这个枚举过程用二分实现。

用线段树求一串数中最长的1串,应该都做过。所以问题就变成了我们如何来建立这个01串,很显然,我们不可能去构造n棵线段树。假设有两个数x,y且x>y那么很显然y的01串是的01串的基础上多添加了几个1,所以我们可以用可持续化线段树来维护当前01串下的最长1串的长度。

下面是程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct ding{
  int h,node;
}a[100009];
struct ding2{
  int l,r,mx,ls,rs,lx,rx;
}f[2000000];
int num,root[100009],n,m;
bool cmp(ding x,ding y){return (x.h==y.h?x.node>y.node:x.h>y.h);}
void build(int& roo,int lef,int righ)
{
  if (roo==0) roo=++num;
  f[roo].l=lef;f[roo].r=righ;
  if (lef==righ) return;
  int mid=(lef+righ)/2;
  build(f[roo].ls,lef,mid); build(f[roo].rs,mid+1,righ);
}
void update(int x)
{
  int lson=f[x].ls,rson=f[x].rs;
  f[x].lx=f[lson].r-f[lson].l+1==f[lson].mx?f[lson].lx+f[rson].lx:f[lson].lx;
  f[x].rx=f[rson].r-f[rson].l+1==f[rson].mx?f[rson].rx+f[lson].rx:f[rson].rx;
  f[x].mx=max(max(f[lson].mx,f[rson].mx),f[lson].rx+f[rson].lx);
}
int insert(int las,int w)
{
  int now=++num;
  f[now]=f[las];
  if ((f[now].l==w)&&(f[now].r==w)){f[now].mx=f[now].lx=f[now].rx=1;return now;}
  int mid=(f[now].l+f[now].r)/2;
  if (w<=mid) f[now].ls=insert(f[las].ls,w);
  else f[now].rs=insert(f[las].rs,w);
  update(now);
  return now;
}
ding2 query(int now,int lef,int righ)
{
  if ((lef<=f[now].l)&&(f[now].r<=righ)) return f[now];
  int mid=(f[now].l+f[now].r)/2;
  if (righ<=mid) return query(f[now].ls,lef,righ);
  else if (lef>mid) return query(f[now].rs,lef,righ);
  else
  {
      ding2 t1=query(f[now].ls,lef,righ),t2=query(f[now].rs,lef,righ),t3;
      t3.l=t1.l; t3.r=t2.r;
      t3.mx=max(t1.rx+t2.lx,max(t1.mx,t2.mx));
      t3.lx=t1.mx==t1.r-t1.l+1?t1.mx+t2.lx:t1.lx;
      t3.rx=t2.mx==t2.r-t2.l+1?t2.mx+t1.rx:t2.rx;
      return t3;
  }
}
int main()
{
  scanf("%d",&n);
  for (int i=1;i<=n;i++)
  {
    scanf("%d",&a[i].h);
    a[i].node=i;
  }
  sort(a+1,a+1+n,cmp);
  num=1;
  build(root[0],1,n);
  for (int i=1;i<=n;i++) root[i]=insert(root[i-1],a[i].node);
  scanf("%d",&m);
  for (int i=1;i<=m;i++)
  {
    int pl,pr,w;
      scanf("%d%d%d",&pl,&pr,&w);
      int nl=1,nr=n;
      while (nl<nr)
      {
        int mid=(nl+nr)/2;
      if (query(root[mid],pl,pr).mx>=w) nr=mid;
      else nl=mid+1;
    }
    printf("%d\n",a[nr].h);
  }
  return 0;
}
时间: 2024-10-29 19:06:21

可持续化线段树(例题Sign on Fence[Codeforces 484E])的相关文章

【poj2104-求区间第k大数(不修改)】主席树/可持续化线段树

第一道主席树~然而是道比较水的...因为它不用修改... 转载一个让我看懂的主席树的讲解吧:http://blog.csdn.net/regina8023/article/details/41910615 (未授权,侵权删) --------------------------------------------------------------------------------------------------------------------- 那么如果要询问i-j之间数字出现的次数

可持续化线段树(主席树)

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

线段树例题及做题误区

学会了一系列的线段树之后发现 除了扫描线还不是很熟之外一些操作基本上是得心应手了. 但是仍是很菜,在此再次深有感悟 以后做题再看题解 直接剁手 我就不信不看题解自己的思路出现错误 每次都当我 有了正确的思路之时 却被一些 很迷的思路 误导去看题解,看完题解之后才恍然大悟 .这点需要注意!!!我想我都窥出正解了为什么不能再多想想呢? 真的是超级没有成就感 感觉是非常难受的 好题被自己一时看了题解毁了这是我作为一个正在学习的人所极不想看见的. . 这道题还不错 对线段树是一个考察 如果能仔细思考的话

区间-&gt;点,点-&gt;区间,线段树优化建图+dijstra Codeforces Round #406 (Div. 2) D

http://codeforces.com/contest/787/problem/D 题目大意:有n个点,三种有向边,这三种有向边一共加在一起有m个,然后起点是s,问,从s到所有点的最短路是多少? 第一种边:u->v w 表示节点u到v有连接一条有向边,权值为w 第二种边:u->[l,r] w  表示节点u到区间[l,r]连接一条有向边,权值为w 第三种边:[l,r]->u w  表示区间[l, r]所有的节点到u都有一条有向边,权值为w 思路: 我们知道,对于dijstra都是用pr

骗分大法之-----分块||迷之线段树例题a

什么是分块呢? 就是一种可以帮你骗到不少分的神奇的算法. 分块的写法有几种,我所知道的有①预处理②不预处理 不预处理的代码我看得一脸懵逼 所以我在这里就谈一下预处理的版本www 首先看一道题: 给定一个包含n个数的序列,初值全为0,现对这个序列有两种操作:操作1:把 给定 第k1 个数改为k2;操作2:查询 从第k1个数到第k2个数得最大值.(k1<=k2<=n) 所有的数都 <=100000 好的,如果我们搞遍历肯定超时到爆炸. 那么就要用到分块大法了 把这n个数分成若干块,然后每个块

主席树(可持续化线段树)

学习粗:https://blog.csdn.net/creatorx/article/details/75446472 题:http://poj.org/problem?id=2104(静态主席树) #include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back inline int read(){ int sum=0,x=1; char ch=getchar(); while(ch

动态开点线段树

用途 需要建立多棵独立的线段树 线段树维护的值域较大(1e9),但是操作次数较少(1e5) 特征 类似主席树的原理,动态分配每个树节点的位置(lson[],rson[]),每次只更新一条链,但是主席树是建立一颗新的树,动态开点线段树是在一棵树上不断添加节点(还是一棵树) 类似线段树的原理,push_down区间修改,push_up区间查询 例题 1.维护值域较大,线段树区间修改 cf915e https://codeforces.com/contest/915/problem/E 题意: q(3

知识点 - 线段树 权值 树套树 二维 可持续

知识点 - 线段树 权值 树套树 二维 可持续 //区间更新求和 inline int ls(int p) { return p << 1; }//左儿子 inline int rs(int p) { return p << 1 | 1; }//右儿子 void push_up(int p) { t[p] = t[ls(p)] + t[rs(p)]; }// 向上不断维护区间操作 void build(ll p, ll l, ll r) { if (l == r) { t[p] =

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

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