2019 ICPC 沈阳区域赛-A Leftbest(STL——set的应用)

Leftbest

Jack is worried about being single for his whole life, so he begins to use a famous dating app. In this app, the user is shown single men/women’s photos one by one, and the user may choose between “yes” and “no”. Choosing “yes” means an invitation while choosing “no” means nothing. The photos would be shown one by one until the number of rest photos to be shown reaches zero. Of course, efficient and single Jack would always choose “yes”.When viewing photos, Jack would have a “fake impression point” on every photo, which is not accurate. To calculate the “true impression point” of one photo, Jack would recall the “fake impression point” of every previous photo whose “fake impression point” is larger than this photo, and regard the smallest “fake impression point” of them as the “true impression point” of this photo. Jack would like to sum the “true impression point” of all photos as the outcome of his effort.Note that if such a larger “fake impression point” does not exist, the “true impression point” of this photo is zero.

Input
The first line contains an integer (1≤n≤100000) — the number of photos.The second line contains n integers a​1​​ , a​2​​ , …, a​n​​ where a​i​​ (0≤a​i​​ ≤10​8​) is the “fake impression point” of the i-th photo.

Output
Output a single integer — the sum of the “true impression point” of all photos.

Sample Input
4
2 1 4 3
Sample Output
6

1.题目大意:给出一个数组,现在每个数的贡献为该数前面大于该数的最小值,求数组所有数的累积贡献

2.真的太太太太太菜了,写了三个多小时。不管是一开始的O(n2)暴力,还是接着想出利用逆序对和排序减少时间复杂度,还是最后背水一战写了双向链表,边维护降序边添加元素…但怎么就没想到set!!!啊,我真的心态崩了,以后绝对不会忘记这道题的做法,铭记于心!

3.正解:首先用set维护升序,接着二分查找前面第一个比它大的数,不用写函数,set里自带了algorithm里的lower_bound和upper_bound,直接使用upper_bound查找即可,如果没找到会返回set::end()迭代器,判断一下即可

4.晚上翻来覆去睡不着,这题目绝对是基础不牢的惨痛教训。那么拓展一下这道题,它还能应用于求每个数前面比它大的最大值;每个数前面比它小的最大值;每个数前面比它小的最小值。而本题是每个数前面比它大的最小值

前缀较大的最小值

这里再复习一下lower_bound()和upper_bound()

//在set中直接传要查询的元素即可

lower_bound(val);        //查找大于等于val第一个元素的位置,没有找到返回set::end()

upper_bound(val);        //查找大于val第一个元素的位置,没有找到返回set::end()
#include <iostream>
#include <set>
using namespace std;
typedef long long ll;
int main(){
    int n,x;
    ll ans=0;
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    set<int> s;
    cin>>n;
    while(n--){
        cin>>x;
        s.insert(x);
        auto i=s.upper_bound(x);
        if(i!=s.end()){
            ans+=*i;
        }
    }
    cout<<ans<<endl;
    return 0;
}

前缀较大的最大值

这里再补充一下几个迭代器分别返回容器的什么位置

begin();     //容器第一个元素的位置

end();       //容器最后一个元素的下一个位置,也就是容器遍历的结束标志,此位置不可访问

rbegin();   //容器逆向的第一个元素,也就是最后一个元素的位置

rend();     //第一个元素的前一个位置
#include <iostream>
#include <set>
using namespace std;
typedef long long ll;
int main(){
    int n,x;
    ll ans=0;
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    set<int> s;
    cin>>n;
    cin>>x;
    s.insert(x);
    n--;
    while(n--){
        cin>>x;
        auto it=s.rbegin();  //不能"auto it=s.end()-1;"会报错
        //auto it =s.end(); it--;
        if(x<*it) ans+=*it;
        s.insert(x);
    }
    cout<<ans<<endl;
    return 0;
}

前缀较小的最大值

#include <iostream>
#include <set>
using namespace std;
typedef long long ll;
int main(){
    int n,x;
    ll ans=0;
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    set<int> s;
    cin>>n;
    while(n--){
        cin>>x;
        auto it=s.lower_bound(x);
        if(it!=s.begin()){
            it--;
            ans+=*it;
        }
        s.insert(x);
    }
    cout<<ans<<endl;
    return 0;
}

前缀较小的最小值

#include <iostream>
#include <set>
using namespace std;
typedef long long ll;
int main(){
    int n,x;
    ll ans=0;
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    set<int> s;
    cin>>n;
    cin>>x;
    s.insert(x);
    n--;
    while(n--){
        cin>>x;
        auto it=s.begin();
        if(x>*it){
            ans+=*it;
        }
        s.insert(x);
    }
    cout<<ans<<endl;
    return 0;
}
发布了128 篇原创文章 · 获赞 7 · 访问量 5251

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/104729214
今日推荐