zoukankan      html  css  js  c++  java
  • UPC-5599 Minimum Sum(线段树求区间最小值+二分区间计数)

    题目描述
    One day, Snuke was given a permutation of length N, a1,a2,…,aN, from his friend.

    Find the following:
    这里写图片描述
    Constraints
    1≤N≤200,000
    (a1,a2,…,aN) is a permutation of (1,2,…,N).
    输入
    The input is given from Standard Input in the following format:
    N
    a1 a2 … aN
    输出
    Print the answer.
    Note that the answer may not fit into a 32-bit integer.
    样例输入
    3
    2 1 3
    样例输出
    9

    题意:给出一个序列,对于这个序列的所有L到R上区间取最小值,然后求和。序列中每个数唯一,并且在1到n之间。

    对于这样的要求,我们想最小值求和,那么先要找到区间,然后对区间找最小值再求和,那么一个最小值可能是几个区间的最小值,所以我们可能会重复加某个数好几遍。因此反过来想,对于一个数,可能在那些区间中是最小值,可能被加和几次。
    在序列中,从1到最小值的位置t,将有t-1+1种包含最小值的区间,在最小值位置t到n这段距离中,存在n-t+1种包含最小值区间的组合。那么所有包含最小值的区间,即
    最小值左半边的区间种类*最小值右半边的区间种类
    比如2 1 3中,2 1 这段左半边有2种区间种类,1 3这段右半边有2种区间种类,通过相互组合,2*2,发现包含1的区间共有4种。那么这4个区间的最小值都是1,则1被加和了4次。

    扩展下去,对于1到n内的最小值是这样计算,那么我们找完了这个范围的最小值,再找不包含整个序列中最小值的两段序列,即 最小值左半边的序列,和最小值右半边的序列,分别在这两个序列中继续找最小值,继续重复计算新的最小值所被包含和计算的区间数量。
    即可得到公式:(t-L+1)*(R-t+1)

    利用上面公式求得每个最小值再一定区间内被计算的次数,这样不断二分成两段区间,不断计算所有值被计算的次数。最后求和即可得到所有区间最小值的总和,注意有爆int的数据。
    用线段树或者RMQ实现区间最小值查询,因为每个数都是唯一的,因此我们将返回的最小值映射到另一个表示该值位序的数组中,方便每次查询最小值之后可以继续找到搜索的新区间边界。

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    struct node
    {
        int l,r,minn;
    } tree[200005*4];
    int a[200004],n,mp[200005];
    LL ans;
    void build(int l,int r,int rt)
    {
        tree[rt].l=l;
        tree[rt].r=r;
        tree[rt].minn=0x7fffffff;
        if(l==r) tree[rt].minn=a[l];
        else
        {
            int mid=(l+r)>>1;
            build(l,mid,rt<<1);
            build(mid+1,r,rt<<1|1);
            tree[rt].minn=min(tree[rt<<1].minn,tree[rt<<1|1].minn);
    
        }
    }
    int query(int l,int r,int rt)
    {
        if(l<=tree[rt].l&&tree[rt].r<=r) return tree[rt].minn;
        else
        {
            int mid=(tree[rt].l+tree[rt].r)>>1;
            int minn=0x7fffffff;
            if(l<=mid) minn=min(query(l,r,rt<<1),minn);
            if(r>=mid+1) minn=min(query(l,r,rt<<1|1),minn);
            return minn;
        }
    }
    void dfs(int l,int r)
    {
        if(r<l) return;
        int mini=mp[query(l,r,1)];
    //    printf("%d===%d==%d
    ",l,mini,r);
        ans+=(LL)(mini-l+1)*(LL)(r-mini+1)*(LL)a[mini];
        dfs(l,mini-1);
        dfs(mini+1,r);
    }
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
            ans=0;
            for(int i=1; i<=n; i++) scanf("%d",&a[i]),mp[a[i]]=i;
            build(1,n,1);
            dfs(1,n);
            printf("%lld
    ",ans);
        }
    }
    
    
  • 相关阅读:
    查看CentOS版本方法
    Android中string.xml文件中设置部分字体颜色大小
    linux重启oracle 各种方法
    Duilib初级控件扩展一例: 具有鼠标滚动消息的OptionUI
    VirtualBox 安装XP虚拟机需要注意的问题
    如何让VS2012编写的程序在XP下运行
    VC 系统托盘编程,含有气泡提示
    获取文件或是文件夹的大小和占用空间
    VC++实现获取文件占用空间大小的两种方法(非文件大小)
    VC++ 获取文件属性创建时间、修改时间和访问时间
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135794.html
Copyright © 2011-2022 走看看