codeforces 573B B. Bear and Blocks(线段树+dp)

题目链接:

codeforces 573B


题目大意:

给出n个连续塔,每个塔有高度hi,每次取走最外层的块,问需要多少次操作能够拿光所有的块。


题目分析:

  • 首先我们可以知道第一次操作时,对于每个塔的变化满足如下的公式:

    hi=min(hi?1,hi?1,hi+1)
  • 每次操作都满足如下的递推式,我们递推一下得到第k次操作第i的塔的高度:

    hi=max(0,minj=0i{min(hi?j?(k?j),hi+j?(k?j)}?hi=max(0,minj=0i{min(hi?j+j?k,hi+j+j?k}

    因为k是常数,所以对于,每一项我们找到的就是最小的hi?j+j和hi+j+j,这可用线段树进行维护。

  • 而且要设定两个虚拟的高度为0的塔,分别放在这一堆塔的两头,这样能够维护两侧的在每次操作拿光的情况。
  • 至于线段树维护的部分,就是遍历i两次,分别从小到大和从大到小,每次修改之前遍历过的位置为所要的状态,也就是之前遍历过的位置区间内全部加1,然后统计[0,i]的最小值,右侧的同理。

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#define MAX 100007

using namespace std;

int n;
int l[MAX],r[MAX],h[MAX];

struct Tree
{
    int l,r,minn,lazy;
}tree[MAX<<2];

void push_up ( int u )
{
    tree[u].minn = min ( tree[u<<1].minn , tree[u<<1|1].minn );
}

void build ( int u , int l , int r )
{
    tree[u].l = l;
    tree[u].r = r;
    tree[u].lazy = 0;
    int mid = l+r>>1;
    if ( l == r )
    {
        tree[u].minn = h[l];
        return;
    }
    build ( u<<1 , l , mid );
    build ( u<<1|1 , mid+1 , r );
    push_up ( u );
}

void push_down ( int u )
{
    int ll = tree[u].lazy;
    tree[u].lazy = 0;
    if ( ll )
    {
        tree[u<<1].lazy += ll;
        tree[u<<1|1].lazy += ll;
        tree[u<<1].minn += ll;
        tree[u<<1|1].minn += ll;
    }
}

void update ( int u , int left , int right )
{
    int l = tree[u].l;
    int r = tree[u].r;
    int mid = l+r>>1;
    if ( left <= l && r <= right )
    {
        tree[u].minn += 1;
        tree[u].lazy += 1;
        return;
    }
    push_down ( u );
    if ( left <= mid && right >= l )
        update ( u<<1 , left , right );
    if ( left <= r && right > mid )
        update ( u<<1|1 , left , right );
    push_up ( u );
}

int query ( int u , int left , int right )
{
    int l = tree[u].l;
    int r = tree[u].r;
    int mid = l+r>>1;
    if ( left <= l && r <= right )
        return tree[u].minn;
    push_down ( u );
    int ret = 1<<29;
    if ( left <= mid && right >= l )
        ret = min ( ret , query ( u<<1 , left , right ) );
    if ( left <= r && right > mid )
        ret = min ( ret , query ( u<<1|1 , left , right ) );
    return ret;
}

int main ( )
{
    while ( ~scanf ( "%d" , &n ) )
    {
        h[0] = h[n+1] = 0;
        for ( int i = 1; i <= n ; i++ )
            scanf ( "%d" , &h[i] );
        build ( 1 , 0 , n+1 );
        for ( int i = 1 ; i <= n ; i++ )
        {
            update ( 1 , 0 , i-1 );
            l[i] = query ( 1 , 0 , i );
        }
        build ( 1 , 0 , n+1 );
        for ( int i = n ; i > 0 ; i-- )
        {
            update ( 1 , i+1 , n+1 );
            r[i] = query ( 1 , i , n+1 );
        }
        int ans = 0;
        for ( int i = 1 ; i <= n ; i++ )
        {
            //cout << "YES : " << l[i] << " " << r[i] << endl;
            ans = max ( ans , min ( l[i] , r[i] ));
        }
        printf ( "%d\n" , ans );
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 01:53:53

codeforces 573B B. Bear and Blocks(线段树+dp)的相关文章

codeforces#426(div1) B - The Bakery (线段树 + dp)

题意:把 n 个数划分成 m 段,要求每组数不相等的数的数量最大之和. 思路: dp方程 : dp[i][j] = max( dp[k][j-1] + v(k, i) );( j<=k<i , k = j, j+1, +...+ i-1) dp[i][j]表示第 i 个数分到第 j 段的最大值. v(k, i) 表示k~i中不同数的个数,此处用hash记录每个数上一次出现的位置,从上一次出现的位置到当前位置的 dp[i][j-1] 值均可+1. 此时时间复杂度 O(n*m*log(n)). 线

codeforces 444 C. DZY Loves Colors(线段树)

题目大意: 1 l r x操作 讲 [l,r]上的节点涂成x颜色,并且每个节点的值都加上 |y-x| y为涂之前的颜色 2 l r  操作,求出[l,r]上的和. 思路分析: 如果一个区间为相同的颜色.那么我们才可以合并操作. 所以我们之前找相同的区间就好. 但是问题是如何合并操作. 那么我们定义一个val  表示这个区间每个位置上应该加上的值. pushdown 的时候这个值是可以相加的. #include <cstdio> #include <iostream> #includ

codeforces 446C DZY Loves Fibonacci Numbers 线段树

假如F[1] = a, F[2] = B, F[n] = F[n - 1] + F[n - 2]. 写成矩阵表示形式可以很快发现F[n] = f[n - 1] * b + f[n - 2] * a. f[n] 是斐波那契数列 也就是我们如果知道一段区间的前两个数增加了多少,可以很快计算出这段区间的第k个数增加了多少 通过简单的公式叠加也能求和 F[n]  = f[n - 1] * b + f[n - 2] * a F[n - 1] = f[n - 2] * b + f[n - 3] * a ..

Codeforces 444C DZY Loves Colors 水线段树

题目链接:点击打开链接 水.. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <math.h> #include <set> #include <vector> #include <map> using namespace std; #define ll long long #defi

CodeForces - 383C Propagating tree(dfs + 线段树)

题目大意: 给出一棵树,树上每个节点都有权值,然后有两个操作. 1 x val 在结点x上加上一个值val,x的儿子加上 -val,x的儿子的儿子加上 - (-val),以此类推. 2 x 问x节点的值. 思路分析: 每个节点上加值都是给自己的儿子节点加,而且这个是颗树. 比如样例上的,如果你给node 1加一个值,那么五个节点都加. 再给node 2加个值,2的儿子节点也加了,之前给1加的值也要加到2号节点的儿子. 所以你会发现节点的儿子会存在一个从属的关系. 这样的话,我们可以把所有节点从新

codeforces Beta Round #19 D. Point (线段树 + set)

题目大意: 对平面上的点进行操作. add x y 在 (x,y )上加一个点. remove x y 移除 (x,y)上的点. find x y 求出在(x,y)右上角离他最近的点,优先级是靠左,靠下. 思路分析: find 操作 比较麻烦. 要保证x大的同时还要确保x最小,而且该x上还要有点. 这样要找大的时候要小的,就是在线段树上选择性的进入左子树还是右子树. 所以核心就是,用set维护叶子节点. 然后查找的时候去叶子节点找,如果这个叶子节点有蛮子的 x y  就输出,否则回溯去另外一个子

Nastya Hasn&#39;t Written a Legend(Codeforces Round #546 (Div. 2)E+线段树)

题目链接 传送门 题面 题意 给你一个\(a\)数组和一个\(k\)数组,进行\(q\)次操作,操作分为两种: 将\(a_i\)增加\(x\),此时如果\(a_{i+1}<a_i+k_i\),那么就将\(a_{i+1}\)变成\(a_i+k_i\),如果\(a_{i+2}<a_i+k_i\),则将\(a_{i+2}\)变成\(a_{i+1}+k_{i+1}\),以此类推. 查询\(\sum\limits_{i=l}^{r}a_i\). 思路 我们首先存下\(k\)数组的前缀和\(sum1\),

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu