zoukankan      html  css  js  c++  java
  • ACM学习历程—HDU5696 区间的价值(分治 && RMQ && 线段树 && 动态规划)

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

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

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

    思路就是我枚举区间左值lt,那么[lt, n]区间内最值的角标分别为mima。于是设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
    ", 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;
    }
    View Code
  • 相关阅读:
    Python3-元组
    Python3-列表
    Python3-字符串
    Python3-for循环机制
    Python3-初识
    优先队列——priority queue
    单调队列 —— 滑动窗口
    SDNU_ACM_ICPC_2021_Winter_Practice_7th [个人赛]
    博弈论入门(论和威佐夫、巴什、尼姆打牌被吊打是什么感受(╥﹏╥)
    字符串最大最小表示法
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/5523080.html
Copyright © 2011-2022 走看看