【AtCoder】AGC006 Minimum Sum

版权声明:本文为博主原创文章,不管你喜不喜欢都请在注明作者后转载~( ̄▽ ̄~) https://blog.csdn.net/C20190102/article/details/82931796

题目

传送门

题目大意

给出一个 N N 的排列 a 1 , a 2 , . . . , a N a_1,a_2,...,a_N ,求 l = 1 N r = l N min { a l , a l + 1 , . . . , a r } \sum\limits_{l=1}^{N}\sum\limits_{r=l}^{N}\min\{a_l,a_{l+1},...,a_r\}

思路

题意就是求序列中每个区间的最小值之和。
考虑有多少个区间的最小值为 a i a_i ,记为 f ( i ) f(i) ,则答案为: i = 1 N a i × f ( i ) \sum\limits_{i=1}^{N}a_i\times f(i)
若区间 [ l , r ] [l,r] 的最小值为 a i a_i ,则其中的每一个数(除了 a i a_i )都比 a i a_i 大。
直接说结论:

  • 找到 a i a_i 左边离它最近 l l ,满足 a l < a i a_l<a_i
  • 找到 a i a_i 右边离它最近 r r ,满足 a r < a i a_r<a_i
  • f ( i ) = ( i l ) × ( r i ) f(i)=(i-l)\times (r-i)

这样找到的区间 ( l , r ) (l,r) (注意是开的)是满足最小值为 a i a_i 的最大的一个区间。因为区间 ( l 1 , r ) (l-1,r) 的最小值就不是 a i a_i 了,是 a l a_l a l < a i a_l<a_i ),同理,区间 ( l , r + 1 ) (l,r+1) 的最小值一定是 a r a_r
但是在区间 ( l , i ) (l,i) ( i , r ) (i,r) 中的每个数都比 a i a_i 大,从 ( l , i ] (l,i] 中选出一个作为左端点, [ i , r ) [i,r) 中选出一个作为右端点,得到 f ( i ) = ( i l ) × ( r i ) f(i)=(i-l)\times (r-i)

于是你发现,暴力完成这个结论还是 O ( N 2 ) O(N^2) 的……


如果我们将比 a i a_i 小的数的下标存在一个set<int> S里面,那么l=S.lower_bound(i)(实现的时候用lower_bound好像会有神奇之事发生,详见代码),r=S.upper_bound(i)
所以将 a a 排个序(和下标一块,用结构体),然后顺着扫,将 a i a_i 前面的数都扔到set里面,再对 a i a_i 的下标(排了序就不是 i i 了)找lower_bound之类就可以了。

似乎有点像偏序……
反正这个问题我想了几百年……

代码

#include<set>
#include<cstdio>
#include<algorithm>
using namespace std;

#define MAXN 200000
int N;
struct node{
    int val,ID;
}A[MAXN+5];

bool cmp(node x,node y){
    return x.val<y.val;
}

int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++){
        A[i].ID=i;
        scanf("%d",&A[i].val);
    }
    sort(A+1,A+N+1,cmp);
    set<int> index;
    index.insert(0),index.insert(N+1);//免得出现找不到的情况
    long long Ans=0;
    for(int i=1;i<=N;i++){
        set<int>::iterator Left,Right;
        Left=Right=index.upper_bound(A[i].ID),Left--;//注意Left的处理
        Ans+=1ll*(A[i].ID-*Left)*(*Right-A[i].ID)*A[i].val;
        index.insert(A[i].ID);
    }
    printf("%lld",Ans);
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/82931796