CF1638C

发布时间 2023-04-24 19:39:03作者: untitled0

蒟蒻有一种用 set 的乱搞(?)做法。

将题目中的连边要求反着说一遍:\(i>j\)\(a_i<a_j\)

考虑遍历 \(a\) 时,遍历到的 \(a_i\) 编号一定要比它前面的大,只需要将前面比它大的元素都和它连起来。

将每个连通块看作一个整体,只要 \(a_i\) 比其中任意一个小,\(a_i\) 就可以加入这个连通块。

因此可以用 set 维护每个连通块的最大值(因为只需要小于最大值即可),在其中 lower_boundupper_bound 出第一个大于 \(a_i\) 的位置。由于所有比 \(a_i\) 大的连通块都要和 \(a_i\) 相连成为同一个,因此将 set 中所有大于 \(a_i\) 的元素都删除,只保留最大的一个代表这个连通块。

剩下的一些细节看代码:

#include<bits/stdc++.h>
using namespace std;
set<int>st;
int main(){
    int t;cin>>t;
    while(t--){
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++){
            int x;scanf("%d",&x);//干脆在线搞,x即为上文a[i]
            auto it=st.upper_bound(x);
            if(it==st.end())st.insert(x);//it==st.end()说明没有比x大的,因此x独立成为一个连通块
            else while(++it!=st.end())//判断当前是否最大的元素
                it=st.erase(--it);//由于刚才++了,现在要--回来。erase的返回值为下一个元素的迭代器
        }
        printf("%d\n",st.size());
        st.clear();
    }
    return 0;
}

时间复杂度:

一次 upper_bound 的时间复杂度为 \(O(\log n)\),共执行 \(n\) 次,bound 总复杂度 \(O(n\log n)\)

一次 erase 的时间复杂度为 \(O(\log n)\),每个元素最多被删除 \(1\) 次,一共最多删除 \(n\) 次,erase 总复杂度 \(O(n\log n)\)

总复杂度 \(O(n\log n)\),可过。