ACM练级日志: CodeForces 414C 归并排序、逆序数和栈内存

来源:互联网 发布:微办公软件 编辑:程序博客网 时间:2024/06/13 12:06

时间一长居然连归并排序都忘了怎么写了…… 偏偏这玩意还不能用STL,必须手写…… 而且这道题又是递归又是取反又是求和,真心麻烦,费了我一个上午才弄出来……期间还遇上了各种各样的问题。


记录一下归并排序:开数组的时候没什么特殊情况就开在全局变量里,然后tmp数组也可以开在全局变量里,由于栈内存的限制,不要把局部变量开得很大,通常栈内存只有可怜的16MB,稍微奢侈一点就爆了……而且爆的特别坑爹,编译器会随便给你标记一行错了,还不让调试,如果不知道是这个问题的话,一辈子也不可能找到到底是哪错了。


这里稍微多说一句,上次做GCJ Round1A的B题时,已经想到了AC算法,就是大数据死活崩溃,估计就是栈内存的事。


递归层数不能过深其实跟这个是一样的道理,栈内存是他俩一块用的。


解决方法有这么几个:第一个是,当然,要是能开成全局的数组最好,这样就是内存总上限管你了;第二就是如果确认实际要处理的东西没那么多,可以用vector,就不会爆了;第三,可以手动扩大栈内存的量…… 这个是我查到的,还没有试验,不知管不管用


在头文件定义的区域加上一句:

#pragma comment(linker, "/STACK:1024000000,1024000000")

也不知道后面两个数什么意思,看着挺吓人的,不过他们都这么写……


然后说这道题,这道题是说一开始有一个长度为2^n的序列,然后不断地划分,前一半长度的数一组,后一半长度的数一组,然后下一层再划分,成为4组…… 直到化为每组只有一个数;接下来有q个操作,每个操作是把一层的所有组组内的数全部逆序,然后每做一次就会问你当前序列的逆序数是多少。


一开始确实想到了归并排序,毕竟这个结构几乎已经告诉你是归并排序了,问题就在于你不能真的去更新每一层的所有节点,如果他不断更新最后一层,那就变成n平方的了。由于一次是对一层操作,所以只需要给每层记录一个数就好。问题就在于这个数怎么定义,由于把每一层逆序时,所有的顺序对和逆序对都互换了,所以每一层只需要存储这一层的顺序对和逆序对即可。注意顺序对+逆序对不一定是对数的总数,因为两个相等的数既不是顺序对也不是逆序对。


然后另一个关键的问题就是谁更新谁。我一开始以为是向上更新,其实是不对的,应该是向下更新。假设现在有8个数,12345678,下设1234  5678 、 12 34 56 78, 1 2 3 4 5 6 7 8, 现在我要把第二层反向,那么可以理解成之后的全反向,变成4321 8765、 43 21 87 65, 4 3 2 1 8 7 6 5,这样才是正确的。不能只改一层,因为每一层逆序对/顺序对的信息其实是 (左边逆序对的数量) +  (右边逆序对的数量) + 本层逆序对的数量,当你逆转一层的时候,后边的关系也变化了,而对于上面的节点来说,“本层逆序对的数量”是没有变的。(该从左边取还从左边取,右边同样)


然后贴一个归并排序求逆序数的模板


void merge(int s, int mid, int e, int level){    int i,j;          int p=s;          i=s;    j=mid+1;          while(i<=mid && j<=e)    {        if(a[i] <= a[j])        {            tmp[p] = a[i];            i++;            p++;        }        else        {            layer[level] += mid+1 - i;//只是求当前层的            tmp[p] = a[j];            j++;            p++;                      }    }    while(i<=mid)    {        tmp[p] = a[i];        i++;        p++;    }    while(j<=e)    {        tmp[p] = a[j];        p++;        j++;    }          for(i=s;i<=e;i++)        a[i] = tmp[i];}  void merge_sort(int s, int e, int level){    if(e==s)        return;    int mid = (s+e) /2;          merge_sort(s, mid, level-1);    merge_sort(mid+1, e, level-1);    merge(s, mid, e, level);}

反正就是每次merge的时候写tmp的一部分,然后把tmp的这一部分还给a就可以了。

求逆序数的时候,其实就是对于每一个左边的数,看一看右边的数里面有几个比他小的就行。这可以用二分搜索做,由于这道题既要求逆序对又要求顺序对,只靠归并是不行的,所以有的人这么做了,但是我自己写发现这样会超时…… 感觉是NlogNlogN的复杂度,有点悬,最后我是归并了两次,一次求顺序对,一次求逆序对,虽然笨拙了点,但是总算是过了……


0 0
原创粉丝点击