【codeforces】484E. Sign on Fence 可持久化线段树

2024-09-05 14:32

本文主要是介绍【codeforces】484E. Sign on Fence 可持久化线段树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

传送门:【codeforces】484E. Sign on Fence


题目分析:看了题解才会呢,感觉太巧妙了~

先对高度从高到低排序,然后构造可持久化线段树,每个节点保存一个高度下对应区间的左连续最大1个数,右连续最大1个数,区间最长连续1个数,然后查询一个区间内最长连续1长度就是线段树区间合并操作。每次询问【L,R,W】就是二分询问的高度(每个高度是一棵线段树),然后看询问区间的连续1个数是否大于等于W,大于就调整上界,否则调整下界(下标小的高度大,这样处理的好处是二分可以很轻易的写出来,直接就是lower_bound)。


不得不叹服啊,自己想了好一会儿也没想到可以转化成连续1来搞。。。

路还很长呢~


代码如下:


#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std ;typedef long long LL ;#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define mid ( ( l + r ) >> 1 )const int MAXN = 100005 ;struct Node {int h , pos ;bool operator < ( const Node& a ) const {return h > a.h ;}
} node[MAXN] ;struct Segment_Tree* null ;
struct Segment_Tree {Segment_Tree* c[2] ;//int sum ;int Lmax ;int Rmax ;int Mmax ;inline void modify ( int val ) {c[0] = c[1] = null ;Lmax = Mmax = Rmax = val ;}inline void push_up ( int l , int r ) {int m = mid ;//sum = c[0]->sum + c[1]->sum ;Lmax = c[0]->Lmax ;Rmax = c[1]->Rmax ;Mmax = max ( max ( c[0]->Mmax , c[1]->Mmax ) , c[0]->Rmax + c[1]->Lmax ) ;if ( Lmax == m - l + 1 ) Lmax += c[1]->Lmax ;if ( Rmax == r - m ) Rmax += c[0]->Rmax ;}
} ;Segment_Tree pool[MAXN * 50] ;
Segment_Tree* cur ;
Segment_Tree* Root[MAXN] ;
int n ;void init () {cur = pool ;null = cur ++ ;null->modify ( 0 ) ;
}void build ( Segment_Tree* &o , int l , int r ) {o = cur ++ ;o->modify ( 0 ) ;if ( l == r ) return ;int m = mid ;build ( o->c[0] , l , m ) ;build ( o->c[1] , m + 1 , r ) ;
}void insert ( Segment_Tree* old , Segment_Tree* &now , int pos , int l , int r ) {now = cur ++ ;if ( l == r ) {now->modify ( 1 ) ;return ;}int m = mid ;if ( pos <= m ) {now->c[1] = old->c[1] ;insert ( old->c[0] , now->c[0] , pos , l , m ) ;} else {now->c[0] = old->c[0] ;insert ( old->c[1] , now->c[1] , pos , m + 1 , r ) ;}now->push_up ( l , r ) ;
}int query ( Segment_Tree* o , int L , int R , int l , int r ) {if ( L <= l && r <= R ) return o->Mmax ;int m = mid ;if ( R <= m ) return query ( o->c[0] , L , R , l , m ) ;if ( m <  L ) return query ( o->c[1] , L , R , m + 1 , r ) ;int ans = max ( query ( o->c[0] , L , R , l , m ) , query ( o->c[1] , L , R , m + 1 , r ) ) ;ans = max ( ans , min ( o->c[0]->Rmax , m - L + 1 ) + min ( o->c[1]->Lmax , R - m ) ) ;return ans ;
}int search ( int L , int R , int w ) {int l = 1 , r = n ;while ( l < r ) {int m = mid ;if ( query ( Root[m] , L , R , 1 , n ) >= w ) r = m ;else l = m + 1 ;}return node[l].h ;
}void solve () {int l , r , w , m ;init () ;For ( i , 1 , n ) {scanf ( "%d" , &node[i].h ) ;node[i].pos = i ;}sort ( node + 1 , node + n + 1 ) ;build ( Root[0] , 1 , n ) ;For ( i , 1 , n ) {insert ( Root[i - 1] , Root[i] , node[i].pos , 1 , n ) ;}scanf ( "%d" , &m ) ;while ( m -- ) {scanf ( "%d%d%d" , &l , &r , &w ) ;printf ( "%d\n" , search ( l , r , w ) ) ;}
}int main () {while ( ~scanf ( "%d" , &n ) ) solve () ;return 0 ;
}


这篇关于【codeforces】484E. Sign on Fence 可持久化线段树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139226

相关文章

Redis事务与数据持久化方式

《Redis事务与数据持久化方式》该文档主要介绍了Redis事务和持久化机制,事务通过将多个命令打包执行,而持久化则通过快照(RDB)和追加式文件(AOF)两种方式将内存数据保存到磁盘,以防止数据丢失... 目录一、Redis 事务1.1 事务本质1.2 数据库事务与redis事务1.2.1 数据库事务1.

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;