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);
    }
}

原文地址:https://www.cnblogs.com/kuronekonano/p/11135794.html