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

Posted nhc2014

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可持续化线段树(例题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;
}

 

 

以上是关于可持续化线段树(例题Sign on Fence[Codeforces 484E])的主要内容,如果未能解决你的问题,请参考以下文章

[CF484E]Sign on Fence

Codeforces Round #276 (Div. 1) E. Sign on Fence 二分答案+主席树

可持久化算法

POJ2374 Fence Obstacle Course 线段树

CF484E Sign on Fence && [国家集训队]middle

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