ACM学习历程—HDU5696 区间的价值(分治 && RMQ && 线段树 && 动态规划)

http://acm.hdu.edu.cn/showproblem.php?pid=5696

这是这次百度之星初赛2B的第一题,但是由于正好打省赛,于是便错过了。加上2A的时候差了一题,当时有思路,但是代码就是过不去。。这次应该是无缘复赛了。。

先不水了,省赛回来,我看了一下这个题,当时有个类似于快排的想法,今天试了一下,勉强AC了。。跑了3S多。

思路就是我枚举区间左值lt,那么[lt, n]区间内最值的角标分别为mi和ma。于是设to = max(mi, ma)。也就是说在to右侧的所有区间[lt, i]的值至少都是a[mi]*a[ma]。用线段树维护长度为i区间的最值,那么我需要用a[mi]*a[ma]去更新区间[to-lt+1, rt-lt+1]在线段树中的值。然后区间就可以缩减为[lt, to-1]了,于是递归求解就可以了,当然此处可以迭代。

关键是上述的递归过程最多需要运行多少次?

首先to这个位置,要么是mi,要么是ma,也就是说左侧的数据要么都比to这个位置的数小,要么都比它大。光看左侧,这个to很像快排一次运行的那个分隔值。那么to平均下来应该是(lt+rt)/2。

那么总的复杂度就是nlognlogn.

但是此处线段树常数较大,所以需要减一下枝,就是当更新值pls比子树中任意值都小,就可以不用更新,维护子树的最小值就可以了。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <string>
#define LL long long

using namespace std;

const int maxN = 100005;
int n, a[maxN];

//RMQ-ST算法
//效率nlogn
//查询区间最值,注意区间[0, n-1]和[1, n]的区别
int ma[maxN][20], mi[maxN][20];

void RMQ()
{
    memset(ma, 0, sizeof(ma));
    memset(mi, 0, sizeof(mi));
    for (int i = 1; i <= n; ++i)
        mi[i][0] = ma[i][0] = i;
    for (int j = 1; (1<<j) <= n; ++j)
        for (int i = 1; i+(1<<j)-1 <= n; ++i)
        {
            if (a[ma[i][j-1]] >= a[ma[i+(1<<(j-1))][j-1]])
                ma[i][j] = ma[i][j-1];
            else
                ma[i][j] = ma[i+(1<<(j-1))][j-1];
            if (a[mi[i][j-1]] <= a[mi[i+(1<<(j-1))][j-1]])
                mi[i][j] = mi[i][j-1];
            else
                mi[i][j] = mi[i+(1<<(j-1))][j-1];
        }
}

int queryMax(int lt, int rt)
{
    int k = 0;
    while ((1<<(k+1)) <= rt-lt+1)
        k++;
    if (a[ma[lt][k]] >= a[ma[rt-(1<<k)+1][k]])
        return ma[lt][k];
    else
        return ma[rt-(1<<k)+1][k];
}

int queryMin(int lt, int rt)
{
    int k = 0;
    while ((1<<(k+1)) <= rt-lt+1)
        k++;
    if (a[mi[lt][k]] <= a[mi[rt-(1<<k)+1][k]])
        return mi[lt][k];
    else
        return mi[rt-(1<<k)+1][k];
}

//线段树
//求区间最值
struct node
{
    int lt, rt;
    LL val, delta;
}tree[4*maxN];

//向下更新
void pushDown(int id)
{
    if (tree[id].delta != 0)
    {
        tree[id<<1].val = tree[id<<1].delta = max(tree[id<<1].val, tree[id].delta);
        tree[id<<1|1].val = tree[id<<1|1].delta = max(tree[id<<1|1].val, tree[id].delta);
        tree[id].delta = 0;
    }
}

//向上更新
void pushUp(int id)
{
    tree[id].val = min(tree[id<<1].val, tree[id<<1|1].val);
}

//建立线段树
void build(int lt, int rt, int id)
{
    tree[id].lt = lt;
    tree[id].rt = rt;
    tree[id].val = 0;//每段的初值,根据题目要求
    tree[id].delta = 0;
    if (lt == rt)
    {
        //tree[id].delta = ??;
        return;
    }
    int mid = (lt+rt)>>1;
    build(lt, mid, id<<1);
    build(mid+1, rt, id<<1|1);
}

//增加区间内每个点固定的值
void change(int lt, int rt, int id, LL pls)
{
    if (pls <= tree[id].val) return;
    if (lt <= tree[id].lt && rt >= tree[id].rt)
    {
        tree[id].val = tree[id].delta = max(tree[id].delta, pls);
        return;
    }
    pushDown(id);
    int mid = (tree[id].lt+tree[id].rt)>>1;
    if (lt <= mid)
        change(lt, rt, id<<1, pls);
    if (rt > mid)
        change(lt, rt, id<<1|1, pls);
    pushUp(id);
}

//查询某段区间内的最值
LL query(int lt, int rt, int id)
{
    if (lt <= tree[id].lt && rt >= tree[id].rt)
        return tree[id].val;
    pushDown(id);
    int mid = (tree[id].lt+tree[id].rt)>>1;
    if (rt <= mid)
        return query(lt, rt, id<<1);
    if (lt > mid)
        return query(lt, rt, id<<1|1);
    return max(query(lt, mid, id<<1), query(mid+1, rt, id<<1|1));
}

void input()
{
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    RMQ();
    build(1, n, 1);
}

int cnt;

void cal(int lt, int rt)
{
    int to, mi, ma;
    while (lt <= rt)
    {
        mi = queryMin(lt, rt);
        ma = queryMax(lt, rt);
        to = max(mi, ma);
        change(to-lt+1, rt-lt+1, 1, (LL)a[mi]*a[ma]);
        rt = to-1;
    }
}

void work()
{
    cnt = 0;
    for (int i = 1; i <= n; ++i)
        cal(i, n);
    for (int i = 1; i <= n; ++i)
        printf("%lld\n", query(i, i, 1));
}

int main()
{
    //freopen("test.out", "w", stdout);
    //freopen("test.in", "r", stdin);
    while (scanf("%d", &n) != EOF)
    {
        input();
        work();
    }
    return 0;
}

时间: 2024-10-13 06:22:45

ACM学习历程—HDU5696 区间的价值(分治 && RMQ && 线段树 && 动态规划)的相关文章

ACM学习历程——HDU2227 Find the nondecreasing subsequences(线段树 &amp;&amp; dp)

Description How many nondecreasing subsequences can you find in the sequence S = {s1, s2, s3, ...., sn} ? For example, we assume that S = {1, 2, 3}, and you can find seven nondecreasing subsequences, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}.

ACM学习历程——POJ3321 Apple Tree(搜索,线段树)

      Description There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been carefully nurturing the big apple tree. The tree has N forks which are connected by bran

ACM学习历程—HDU 5443 The Water Problem(RMQ)(2015长春网赛1007题)

Problem Description In Land waterless, water is a very limited resource. People always fight for the biggest source of water. Given a sequence of water sources with a1,a2,a3,...,an representing the size of the water source. Given a set of queries eac

ACM学习历程—HDU 5536 Chip Factory(xor &amp;&amp; 字典树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5536 题目大意是给了一个序列,求(si+sj)^sk的最大值. 首先n有1000,暴力理论上是不行的. 此外题目中说大数据只有10组,小数据最多n只有100.(那么c*n^2的复杂度应该差不多) 于是可以考虑枚举i和j,然后匹配k. 于是可以先把所有s[k]全部存进一个字典树, 然后枚举s[i]和s[j],由于i.j.k互不相等,于是先从字典树里面删掉s[i]和s[j],然后对s[i]+s[j]这个

ACM学习历程—HDU 4726 Kia&#39;s Calculation( 贪心&amp;&amp;计数排序)

DescriptionDoctor Ghee is teaching Kia how to calculate the sum of two integers. But Kia is so careless and alway forget to carry a number when the sum of two digits exceeds 9. For example, when she calculates 4567+5789, she will get 9246, and for 12

ACM学习历程—HDU 5023 A Corrupt Mayor&#39;s Performance Art(广州赛区网赛)(线段树)

Problem Description Corrupt governors always find ways to get dirty money. Paint something, then sell the worthless painting at a high price to someone who wants to bribe him/her on an auction, this seemed a safe way for mayor X to make money. Becaus

ACM学习历程—UESTC 1226 Huatuo&#39;s Medicine(数学)(2015CCPC L)

题目链接:http://acm.uestc.edu.cn/#/problem/show/1226 题目就是构造一个对称的串,除了中间的那个只有1个,其余的两边都是对称的两个,自然答案就是2*n-1. 代码: #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #

线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

闲话 stO猫锟学长,满脑子神仙DS 线段树分治思想 我们在做CDQ的时候,将询问和操作通通视为元素,在归并过程中统计左边的操作对右边的询问的贡献. 而在线段树分治中,询问被固定了.按时间轴确定好询问的序列以后,我们还需要所有的操作都会影响一个时间区间.而这个区间,毫无疑问正好对应着询问的一段区间. 于是,我们可以将每一个操作丢到若干询问里做区间修改了,而线段树可以高效地维护.我们开一个叶子节点下标为询问排列的线段树,作为分治过程的底层结构. 具体的实现,仍然要看题目. 例题1 BZOJ4025

SPOJ 1043 Can you answer these queries I 求任意区间最大连续子段和 线段树

题目链接:点击打开链接 维护区间左起连续的最大和,右起连续的和.. #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <math.h> #include <vector> #include <map> using namespace std; #define N 50050 #define Lson