• CDQZ.OPENJUDGE DataStructure22


      我觉得这是很重要的一些题目。它们都在这里:硕巨结构

    Challenge 0:给定数列,单点修改,单点查询修改。煞有介事,不过一数组耳。

    Challenge 1:给定数列,单点修改,单点查询第k次操作后的值。当时写了链表,显然可以被卡。lemonoil说写主席树才是正道,其实不然。使用vector<pair<int,int> > ...[n]就可以了。插入显然都是有序的,查询直接二分就好了。

    Challenge 2:数列最开始是空的,允许在最后插入一个数,单点询问,或撤销若干次操作(包括撤销与插入操作)。主席树很好写,主要是考察对“撤销”的理解(最开始就是不懂“撤销”,以为是直接把顶上的东西丢了)。其实不管插入还是撤销,总的操作数都加了1。而插入是基于前一次操作再加入一个数,而撤销是把若干次以前的再搬回顶上。使用一个len来辅助维护数列的长度。

    Challenge 3:给定数列,单点修改,区间查询和。树状数组就好了,不必写线段树。

    Challenge 4:给定数列,单点修改,区间查询相邻两数乘积和,区间查询任意两数乘积和,%1e9+7。记得这个当时调了很久很绝望,但最后发现错误很小很微不足道,就是所有单点的值都应该%MOD+MOD%MOD。因为如果不把这些数标准化,得到的也不会是标准的结果。而当时写struct写的很痛苦,后面写的直接把pair给typedef了,再加上预带的merge函数下面就可以变得很清新。这种套路也出现再IDY4月份的有序链剖中。

    Challenge 5:给定数列,区间修改成同一个数,区间查询和。线段数的模板题,看似很简单。但就是这样的题目也是有坑点的。因为修改成的数可以为所欲为,所以常规状态下的什么-1就是大错特错了。要么再加一个bool变量,要么让flag的初始值变成绝对不可能取到的值。

    Challenge 6:这道题目就是BZOJ2957楼房重建。IDY4月份当时还考过这道题目的进阶版本:单点修改,区间询问。首先,每个线段存的是区间最大值与区间可以看见的数目。而又有一个很重要的内函数,询问一个线段中高度>h的楼房数,也是二分(或分治)递归单次O(lgn),中间还是很有机巧的(充分利用题目性质)。这个东西在query和update都用得很多,于是整个是O(nlg2n)的。

      而全局询问和区间询问的差别在哪里?对于后者,IDY的做法是把覆盖线段通通fetch出来再for一遍;而QJX告诉我,其实直接搞就是了。对于跨中点的询问,先处理左边,修正一下影响再处理右边,好像有一点CDQ的意思。

      而此题其实可以区间修改。区间修改包括区间修改为同一个值和区间同增同减,我通常的做法是使用“kill”来打扫干净屋子再请客,而中间的顺序是很有意思的。最后写出来很长,大概可以用来考后来人了。

      另外,此题使用分块也是可以的。

    Challenge 7:给定数列,单点修改,询问一个数出现的次数。使用map可以过,但最开始使用multiset却稳稳的TLE。先以为是count过慢(但count确实非常慢),但直到做完Challenge 8才知道是为什么。

      但COUNT确实很慢,我在自己的机子上试了一下这段代码:

     1 #include <cstdio>
     2 #include <set>
     3 #include <ctime>
     4 
     5 std::multiset<int> st;
     6 int main() {
     7     for( int i = 1, j; i <= 100000; i++ ) {
     8         if(i%1000==0) printf("%d %d
    ",i,clock());
     9         st.insert(100);j=st.count(100);
    10     }
    11     printf("%d
    ",clock());
    12

      得出了这样一张表:

    进行n次插入与count 时间(ms)
    1000 5
    5000 101
    10000 407
    20000 1631
    40000 6708
    60000 15261
    80000 28005
    100000 43916

    Challenge 8:给定一个集合,允许删除或插入数(不可重),询问集合中任意两数差绝对值的最小值。使用1个set存数字,1个multiset存答案。中间有曾经XGG那道巡游和YALI那道容斥集合的影子。但是最开始交上去直接TLE了,本地测试发现插入仅10000个数就会TLE,再排查就发现去掉lower_bound速度会快上很多很多很多倍。于是整个人迷惑了,不是O(logn)的么?在网上查,找到了一篇很好的博客:std::set::lower_bound与std::lower_bound的效率问题。长见识了,所以不应该std::lower_bound(st.begin(),st.end(),...)了,而是应该st.lower_bound(...)啊。

    Challenge 9:给定数列,单点删除,询问单点。典型的简单平衡树。

    Challenge 10:给定数列,单点修改,询问全局第k大。使用值域线段树或是平衡树值域线段树需要注意对边界的处理(就像cheese那样虽然最后数据很水),使用-0x7fffffff~0x7fffffff是1000ms,使用0~0xfffffffe用unsigned大概快50~100ms。而平衡树可以不用建新节点,把原来的取出来,让它变成叶子结点(我在做phalanx的时候就是忘了改它的左右儿子和siz),再插回去。快得多,只要了380ms,但代码长度大约90行比线段树多一半。

    Challenge 11:简单主席树。ZJC说可以用“可持久化树状数组”。

    Challenge 12:经典题目。区间第k大无修改无插入。使用树状数组+线段树固然可以,但毕竟是O(nlog2n)的,时间4159ms,空间也大的吓人(开始就是空间开小了RE)。只使用主席树虽然无法修改,但复杂度就是O(nlogn)的了,时间1048ms。如果使用莫队,因为还是需要辅助数据结构,而O(nsqrtnlogn)素来是跑不过100000的。但是,这样就完了?不不不。还有一种神方法。使用区间线段树,每个线段一个vector,build的时候自下向上进行归并。查询的时候把log个线段搞出来,外面二分答案,内部for一遍加上upperbound。看上去是O(nlg3n)的,但其实就应该是O(nlg3n)的。但是数据如果强度不够高,水过去还是可以的。

  • 相关阅读:
    面试问题之C++语言:C++中指针和引用的区别
    手撕代码:最长回文子串
    手撕代码:求字符串最长回文子序列
    手撕代码:用宏来实现获取数组的大小
    手撕代码之线程:thread类简单使用
    面试问题之计算机网络:OSI七层网络模型及相关协议
    C++各种输入
    C printf格式化输出
    记一次mac 安装MySQL-python 的惨痛经历
    记一次tomcat程序运行慢的处理过程
  • 原文地址:https://www.cnblogs.com/Doggu/p/DS22.html
Copyright © 2020-2023  润新知