黑书笔记
就从P77开始记好了.(稍微扩容)
P77
排序.
1.香农信息论
基于比较的排序不可能突破O(nlogn)的复杂度限制.
1) 比较一次可以得到一个bool量,最大信息熵为$\log_2{2}=1.0$bit
2) 全排列的个数是$n!$级别的,唯一地确定一个全排列需要$O\left(\log_2{n!}\right)=O(n\log{n})$bit的信息熵
3) 因此比较的次数不可能少于$O(n\log{n})$级别.
2.实数比较的$O(n\log{\log{n}})$时间算法.
这个的资料我没有找到,如果有谁找到了请告诉我.我只找到了两三篇整数排序的论文.
1) 时间复杂度$O(n\log{\log{n}})$,链接: http://pan.baidu.com/s/1i3vhA0P 密码: atfv
2) 期望时间复杂度$O(n\sqrt{\log{\log{n}}})$,链接: http://pan.baidu.com/s/1jGmYdSi 密码: irxb
UPD: 第三篇
3) 时间复杂度$O(n\log{\log{n}})$,$O(n)$,Parallel,...,空间复杂度$O(n)$,链接: http://pan.baidu.com/s/1qWkJVGw 密码: qmca
(神级论文)
(论文地址请不要转载,谢谢.)
整数的排序可以使用vEB树,也可以使用一种据2)中说的"简单"的算法3).
P78
1.POI
Polish Olympaid in Informatics
避免邪教污染,这里给出了地址.
P87
1.并查集删除一个元素
我们对于每个并查集节点设置一个Disabled属性即可.
当删除一个节点时,将它的Disabled属性设置为true,并重新建立一个节点,将原节点查找指针指向新节点.($O(1)$)
如果删除的是root,我们遵循Lazy updating的原则;在第一个访问到root是一个Disabled节点时将这个root的father指向这个节点,把这个节点设置为新的root即可.($O(1)$)
(A deletable disjoint-set implementation,still $O(n\alpha{n})$)注意使用前将len和rlen赋值为1.
struct node{ int father,disabled,rank,reid; }; struct set{ node p[1000000]; int len,rlen,q[1000000]; int makeSet(){ p[rlen].reid=len; q[rlen]=len++; return rlen++; } int makeVirtualPoint(int n){ p[rlen].reid=n; q[rlen]=n; return rlen++; } int findNaive(int k){ if(p[k].father) return p[k].father=findNaive(p[k].father); return k; } inline void merge(int a,int b){ a=findNaive(a),b=findNaive(b); if(a!=b){ if(p[a].rank<p[b].rank) std::swap(a,b); p[b].father=a; ++p[a].rank; } } inline int find(int k){ int d=findNaive(k); if(p[d].disabled){ p[d].father=k; p[k].father=0; } return findNaive(d); } void del(int i){ q[p[i].reid]=makeVirtualPoint(p[i].reid); p[i].disabled=1; } };
2. 1.4.2 & 1.4.3
1.4.2) 没试过...
1.4.3) 这个均摊时间复杂度是错误的,因为它无法保证k的大小,当n=1时,假设原数字是$2^{k-1}-1$,此时的操作时间复杂度为$O(k)=O(kn)$.均摊复杂度需要对于任意操作序列都有$T(n)=O(n)$,而显然长度为1的操作序列也是合法的,但是此时不成立,因此均摊分析是错误的.
3.穿线森林(Threaded Forest Disjoint-set)
我们可以对每一棵堆建立一个链表,然后启发式合并(将小的合并到大的上).这个很简单的,有一篇大学的讲稿:链接: http://pan.baidu.com/s/1sjE4jA9 密码: c8n3
穿线森林很难实现删除操作,因为无法动态维护Leader指针.
(A Threaded Forest implementation)
struct node{ int rank,leader,next,last; }; struct set{ node p[1000000]; int len; inline int makeSet(){ ++len,p[len].last=p[len].leader=len,p[len].rank=1; return len; } inline int find(int a){ return p[a].leader; } inline void merge(int a,int b){ a=find(a),b=find(b); if(a!=b){ if(p[a].rank < p[b].rank) std::swap(a,b); int c=p[a].next,d=p[b].last; p[a].next=b; p[a].rank+=p[b].rank; while(b!=d){ p[b].leader=a; b=p[b].next; } p[d].leader=a; p[d].next=c; if(!c) p[a].last=d; } } };
4.POI窗户
因没有数据范围,仅提供思路.
我们将顶点坐标排序,用根离散化的扫描线处理.在处理前我们要先在通过窗户上下界的与上下界垂直的线段的垂足上加点,以处理边界情况.从上界往下界(或反过来),边扫描边合并矩形.到另一个界的时候查看下有几个联通块即可.
P88
1.CEOI1999 奇数偶数
这个专题是并查集,那么往并查集的方向去想.
那么这道题就是用并查集维护奇偶性数据,并动态合并.动态合并是一个难点,但也不是不可以解决.方法很简单,我们把闭区间转化为半开半闭区间就可以无压力地合并了.
A) 如果两个端点都是同一个联通块的端点,那么他们的奇偶性已经可以直接用异或运算查询出来.
B) 如果两个端点不是同一个联通块,就把小的联通块并入大的那个,并修改小的的奇偶性以吻合.
那么通过一个$O(1)$的merge操作怎么能保证A)中的性质呢?还是Lazy updating,方法是边路径压缩边以父亲节点为准更新.为什么这样做是正确的?我们的题目要求尽量处理前面的序列,中间不能断开,也就是现有的状态其实都是合法的.那么最后更新的节点肯定是根节点,那么我们只需要查询时Lazy地保持与根节点不冲突即可.
我们可以非常方便地维护奇偶性,只需要异或运算.
P90
1.CEOI2003 赛车(the race)
1) 求逆序对数可以用树状数组解决.
2) 安利一下好写的二项堆.
3) 建议大神实现Rank-pairing Heap以增长RP.
2.可怜的奶牛
1) lrj的OIBH已经不在了.
2) 这道题是按模n同余类分块牛分别处理每日产量最少牛.
P92
1) 待理解:prefixless语言.
P94
1. 思考题1.4.8 & 1.4.9
1) 二叉堆无法方便的合并.(其实也可以,变成松散的堆即可)
2) Treap.
2. 思考题1.4.10 & 1.4.11
1) 1.4.10不会...
2) 还是不会...
3.(CEOI2003)方格
这题最重要的条件就是源点到任何点的所有路径长度都相等.那么我们就可以利用这个性质快速递推出相邻格子的距离,只需要一次询问.
既然这样,我们就可以设计出一个算法:一直往下走直到走的距离大于L(碰到底了就往右走),再每次向上(D>L)或向右(D<L)调整直到D=L.
P95
1.仓库
把钩子公式带进去算即可.
P96
1.字符串哈希
BYV写过一片比较:https://www.byvoid.com/blog/string-hash-compare.
推荐使用BKDRHash.代码比ELFHash要短.
unsigned int BKDRHash(char *str) { unsigned int seed = 131; // 31 131 1313 13131 131313 etc.. unsigned int hash = 0; while (*str) { hash = hash * seed + (*str++); } return (hash & 0x7FFFFFFF); }
BYV说程序不是他写的.
2.马尔可夫链
这题Hash傻逼题,不说了.
P100
1.POI2000 Promotion
可以使用rank-pairing heap维护.时间复杂度应该是最优的了.
add | $O(1)$ |
del | $O(\log{n})$ |
getmin | $O(1)$ |
getmax | $O(1)$ |
可以用两个二叉堆.注意到二叉堆兹瓷删除任意一个元素.时间复杂度都是$O(log n)$.
可以用分块哈希.
P102
1.采矿
这个算法好像有点问题我要研究研究...
2.思考题1.4.14 & 1.4.15
1) 可以用区间合并的平衡树来维护.
我们假设一个节点的附加域为(MinDecField,smallest,largest).
显然,a.MinDecField=min{right(a).smallest-a.value,a.value-left(a).largest,right(a).MinDecField,left(a).MinDecField}.
答案就是root.MinDecField.
推荐用Splay(Spaly?这个梗不好玩了)实现.
2) 分布应尽量均匀随机.当然受到Hash函数的值域大小限制了.
附: 字符串Hash不如直接Trie.
3.1.4.16 天上的星星
仍然扫描线大法好.
从左到右扫描,每次从下往上加点.每次加点前先算出点的等级:查询出已有的纵座标小于当前点的点个数.
P105
1.ACM/ICPC2000上海区域赛 围棋Weiqi(其实围棋英文为Go)
1) 吐槽:围棋Go game和**游戏(Galgame)读起来很像.
2) 我怎么感觉这题是LCT题啊...果然我是蒻= =
P106,P107
P108
1) 没有什么问题就不写了.