洛谷P1801 黑匣子

一、前言

        最近发现了一个神奇的东西——二分插入法,二分插入法虽然在很久以前就学了,但是那个时候没有意识到 log 优化何其强大,所以对这种算法印象不够深刻,一度忘记了这种算法,后来在做题时发现了有些时候可以用二分插入法直接水过数据结构题,因为平衡树本身就有常数,所以貌似数据也没有办法开很大,所以这种做法虽然常数略大,但是在很多时候都可以卡过去,加上个快读基本上和树也差不了太多了,而且二分法 c++ STL有提供,所以代码量也会小的惊人,甚至再插入操作比较少的时候可以比树还快,别人辛辛苦苦敲了一两百行结果被你四十行就a掉了,这里就可以节约大量的时间。

二、题解

       随便浏览了一下题解区,我发现貌似是没有我这种做法的,但是题解已经满了,所以发不了题解了,于是乎来博客园水一发题解。

       二分插入法就是插入法的一个优化版本,即使用二分法来查找插入数据的位置代替掉枚举查找。因为二分的复杂度是 logn 所以插入法的复杂度就被优化成了 nlogn 单次插入 logn,是不是很熟悉,没错这不就是在平衡树上插入数据时的复杂度吗。当然由于有移位操作,所以复杂度不是 logn。但是没关系,前面提到平衡树的各种旋转操作会有常数,所以不会慢太多。具体慢多久要看插入操作的次数的数量了,个人感觉这种方法在很大程度上可以取代平衡树。如果只是要求插入数据,查询排名,前驱后继这样的问题可以直接使用二分插入法维护一个有序数列,插入用 insert,二分用 upper_bound 就行了,节约了一百行左右,真爽,哈哈哈哈。

上代码:

#include<bits/stdc++.h>
using namespace std;
vector<int>arr;
int a[200001];
int question[200001];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",a+i);
    }
    int c = 0;
    for(int i = 0;i < m;i++)
    {
        int x;
        scanf("%d",&x);
        question[x]++;
    }
    for(int i = 1;i <= n;i++)
    {
        arr.insert(upper_bound(arr.begin(),arr.end(),a[i]),a[i]);
        while(question[i])
        {
            cout<<arr[c]<<endl;
            question[i]--;
            c++;
        }
    }
}

本来是想找个用堆的题做做的,结果用了特殊手段。。。。当然这代码不是最优的,还有一些地方可以优化,但我懒。反正复杂度也没有达到1e8级别,索性就不优化了。

原文地址:https://www.cnblogs.com/KALY/p/12438689.html