【算法】【stl】

12 阅读4分钟

vector

Simons and Posting Blogs


题目大意

发布一篇博客时,会按顺序处理博客里提到的用户:

  • 如果这个用户已经在 当前队列 QQ 里,就把它移动到 QQ 的最前面(“提到”一次,相当于“激活”一下它)。
  • 如果这个用户不在 QQ 里,就把它插入到 QQ 的最前面。

我们要选择 博客发布的顺序,使得最终 QQ 的字典序最小。

代码

#include <bits/stdc++.h>  
using namespace std;  
using ll=long long;  
void so(){  
    int n;cin>>n;  
    vector<vector<int>>a(n,vector<int>{});  
    for(int i=0;i<n;i++){  
        int l;cin>>l;  
        set<int>st;  
        for(int j=0;j<l;j++){  
            int x;cin>>x;a[i].push_back(x);  
        }reverse(a[i].begin(),a[i].end());  
        vector<int>rep;  
        for(int j=0;j<l;j++){  
            if(st.empty()||st.find(a[i][j])==st.end()){  
                rep.push_back(a[i][j]);  
                st.insert(a[i][j]);  
            }  
        }a[i]=rep;  
    }sort(a.begin(),a.end());  
    vector<int>ans;  
    map<int,int>seen;  
    while(true){  
        if(a.empty())break;  
        for(int i:a[0]){  
            if(seen[i]==0){  
                seen[i]=1;  
                ans.push_back(i);  
            }  
        }vector<vector<int>>repp;  
        for(int i=1;i<a.size();i++){  
            vector<int>rep;  
            for(int j=0;j<a[i].size();j++){  
                if(!seen[a[i][j]])rep.push_back(a[i][j]);  
            }repp.push_back(rep);  
        }a=repp;  
        sort(a.begin(),a.end());  
    }  
    for(int i:ans){cout<<i<<' ';}  
    cout<<'\n';  
}int main(){  
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);  
    int t;cin>>t;  
    while(t--){so();}  
}

思路

1.总体思路:把b[i]逆序后排序,字典序小的优先出现,然后去掉已经出现过的,重复这一过程(由数据大小知)

stl

1. set
set<int> st;
st.empty()
st.find(a[i][j]) == st.end()
st.insert(a[i][j])
  • 有序集合,元素不重复,自动排序(默认升序)。
  • st.empty():判断集合是否为空。
  • st.find(x):查找 x,返回迭代器,如果没找到返回 st.end()
  • st.insert(x):插入元素,如果已存在则插入失败(但这里没检查返回值)。

在这里用于 去除同一篇博客内重复的用户(只保留第一次出现的)。


2. map
map<int,int> seen;
seen[i] == 0
seen[i] = 1
  • 键值对映射,自动按键排序。
  • map<int,int> 的键是用户 ID,值是标记(0 未出现,1 已出现)。
  • seen[i]:如果键 i 不存在,会默认初始化为 0(因为 int 的默认构造是 0)。
  • 用来标记一个用户是否已经被加入最终答案。

3. sort
sort(a.begin(), a.end());
  • 排序算法,默认升序。
  • vector<vector<int>> 排序时,按字典序比较两个 vector<int>
  • 这里用于每次循环后重新排序剩余博客,保证每次取第一个博客(即当前字典序最小的博客)来处理。

Portal


题目大意

  • 传送门的位置定义(2):
    • 在位置 ( i ) 的传送门,位于数组第 ( i ) 个元素和第 ( i+1 ) 个元素之间。

允许的两种操作

  1. 操作 1
    从一个传送门的紧左边取一个元素,插入到另一个传送门的紧右边。

  2. 操作 2
    从一个传送门的紧右边取一个元素,插入到另一个传送门的紧左边。

目标
通过任意次这些操作,得到 字典序最小 的排列

代码

#include <bits/stdc++.h>  
using namespace std;  
using ll=long long;  
void so(){  
    int n,x,y;  
    cin>>n>>x>>y;  
    x--;y--;  
    vector<int>a,b;  
    for(int i=0;i<n;i++){  
        int j;cin>>j;  
        if(i<=x||i>y)a.push_back(j);  
        else b.push_back(j);  
    }if(!b.empty())rotate(b.begin(),min_element(b.begin(),b.end()),b.end());  
    //将b中的最小元素放开头  
    int m=b.empty()?-1:b[0];  
    auto it=a.begin();  
    while(it!=a.end()&&*it<m)it++;  
    a.insert(it,b.begin(),b.end());  
    for(int i=0;i<n;i++){  
        cout<<a[i]<<' ';  
    }cout<<'\n';  
}int main(){  
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);  
    int t;cin>>t;  
    while(t--){so();}  
}

思路

1.分成两边和中间

2.中间=最小元素+其他元素顺时针旋转(这点没想到,还以为是拼接)

3.前面的段<m

4.要用两个数组存使代码更简短

stl

这道题用到了两个 C++ STL 算法,我来帮你总结它们的用法和在这道题中的作用。


1. std::rotate 的作用与用法

原型

template< class ForwardIt >
ForwardIt rotate( ForwardIt first, ForwardIt n_first, ForwardIt last );

作用 将区间 [first, last) 内的元素循环左移,使得原来在 n_first 位置的元素成为新的第一个元素。
相当于把 [first, n_first) 的元素移到 [n_first, last) 之后。

2. std::vector::insert 的作用与用法

原型

iterator insert( const_iterator pos, InputIt first, InputIt last );

作用[first, last) 范围内的元素,插入到 pos 位置之前。
返回指向第一个插入元素的迭代器。


Chimpanzini Bananini


题目大意

这道题我来帮你概括一下题意。

有一个数组,初始为空。有三种操作:

  1. 循环右移[a1, a2, ..., an][an, a1, a2, ..., an-1]
  2. 反转数组[a1, a2, ..., an][an, an-1, ..., a1]
  3. 追加元素:在数组末尾添加一个元素 k

每次操作后,需要计算数组的 rizziness

rizziness = ∑(b[i] * i)  (1-indexed)

即每个元素乘以它的位置(从1开始)的总和。

代码

#include <bits/stdc++.h>  
using namespace std;  
#define int long long  
using ll=long long;  
void so(){  
    int ml=0;  
    int rev=0;  
    int q;cin>>q;  
    int tot=0;  
    int n=0;  
    deque<int>qn,qr;  
    while(q--){  
        int s;cin>>s;  
        if(s==1){  
            int la=qn.back();  
            qn.pop_back();  
            qn.push_front(la);//将最后一个弹到开头  
            ml+=(tot-la);//增量  
            ml-=la*n;  
            ml+=la;//出来的  
              
            la=qr.front();  
            qr.pop_front();  
            qr.push_back(la);  
              
            rev-=(tot-la);  
            rev+=la*n;  
            rev-=la;  
        }else if(s==2){  
            swap(rev,ml);  
            swap(qn,qr);  
        }else if(s==3){  
            n++;  
            int k;cin>>k;  
            qn.push_back(k);  
            qr.push_front(k);  
            ml+=k*n;  
            rev+=tot;  
            rev+=k;  
            tot+=k;  
        }cout<<ml<<'\n';  
    }  
}signed main(){  
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);  
    int t;cin>>t;  
    while(t--){so();}  
}

思路

比起reverse一次的反转,需要n的复杂度重新计算魅力值,一开始就保存反转的数组进行反转

注意保存所有值的总和便于反转

stl

注意deque

push_back()

push_front()

pop_back()

pop_front()