[关闭]
@totorato 2019-04-05T07:48:02.000000Z 字数 8096 阅读 785

notes

c++ 将函数名作为函数参数的具体操作

1.直接使用

  1. int f(int a){rturn a+1;}
  2. int use_f(int a,int (*func)(int)){return f(a);}
  3. int main()
  4. {
  5. int a;
  6. cin>>a;
  7. cout<<use_f(a,f)<<endl;
  8. }

2.在结构体中使用
由于在结构体中直接如上的使用报错“非静态对象不能这样使用”,所以需要加上static

  1. struct test
  2. {
  3. static int f(int a){rturn a+1;}
  4. int use_f(int a,int (*func)(int)){return f(a);}
  5. };
  6. test T;
  7. int main()
  8. {
  9. int a;
  10. cin>>a;
  11. cout<<T.use_f(a,T.f)<<endl;
  12. }

对于“删除某元素后剩余元素的运算值”问题的快速解法

如果答案可以通过增量法快速求出,则可以:
用f[i][j]表示删除了i到j后剩余元素的运算值,通过f[i][j]求出f[i][mid]和f[mid+1][j]。总的时间复杂度为O(nlognf(n)),其中f(n)为新增加一个元素的时间复杂度。
这个方法可以用在:

模意义下的矩阵消元(上三角化)

如果模数是素数:使用费马小定理求逆元进行除法。
如果模数不是素数:辗转相减。

LIS类问题的树上求解

利用multiset

对序列求lis时,我们可以将数字依次放进multiset中,每次新加入一个数字,删除其lower_bound,插入其本身。
对树求lis时,启发式合并每个字树的multiset,删除父节点lower_bound,插入父节点。

竞赛图

仅给出定理1的证明思路:
运用数学归纳法,当(n-1)个点的竞赛图满足定理1时,我们证明n个点的竞赛图也满足定理1.
添加一个点后,此点必定与之前的点的连边既有入边也有出边。假设全部都是出边,现给定一个入边。这样一定使该图存在一个n简单元环。然后证明对于该点存在[3,n-1]元环。
反证法:对于经过该点的k元环,确定之前n-1个点的n-1元环,那么在环上每隔k-1个点都必须与该点连入边。如果,则一定可以找到这样的环。如果,则通过这些连入边的点之间的连边,也一定可以找到一个k元环。

对于竞赛图类的问题,一般在拓扑排序后对图进行分析。

例题,test20180926,求双色边的竞赛图是否满足,每一种颜色的子图中都满足若存在则一定存在。该题利用到如下结论:该性质存在当且仅当在一个子图中联通的两点在另一个子图中不连通,且每个子图不存在环。因此,将两个图正反合并在一起,找是否存在环即可。直接传递闭包最快是的,而这种方法可以做到

点分治

由于动态点分治中子树之根并不连续,所以必须利用LCA求原树上的树上距离。
RMQ求LCA时需要在遍历子节点的同时不停在dfs序列内插入当前节点以维持最小值的准确。这样只会带来2的常数。

LCT

lct最灵活之处在于可以动态维护一棵树的形态(利用区间翻转标记)。

  1. void access(int a)
  2. {
  3. int b=0;
  4. while(a)
  5. {
  6. spl(a);
  7. tre[a].s[1]=b;
  8. upd(a);
  9. b=a;
  10. a=tre[a].f;
  11. }
  12. }
  13. void sroot(int a)
  14. {
  15. access(a);
  16. spl(a);
  17. tre[a].rv^=1;
  18. }
  19. void cut(int a,int b)
  20. {
  21. sroot(a);
  22. access(b);
  23. spl(b);
  24. tre[b].s[0]=tre[a].f=0;
  25. upd(b);
  26. }
  27. void link(int a,int b)
  28. {
  29. sroot(a);
  30. tre[a].f=b;
  31. }
  32. int qmax(int a,int b)
  33. {
  34. sroot(a);
  35. access(b);
  36. spl(b);
  37. return tre[b].mxp;
  38. }

尺取法

利用类似“毛毛虫”的一种解构维护一段连续的东西的方法。

这样,在平移这个“尺取”出的毛毛虫时,我们可以只是简单的处理左右端点的变化,这样可以依靠题目原本的单调性使总复杂度变成

分块枚举法:

对于一类需要多次进行“存储”与访问的操作,有时存储耗时多,而访问极其容易;或者访问耗时多,存储极其容易。

比如在一个数不超过的集合里寻找x的倍数的个数,且已知所有数都是一个以内数的约数,这时访问操作变成了或者的了,而存储为,这时我们可以让存储让一步,使访问变快,最终两者速度达到平衡,利用均值不等式,我们知道两者复复杂度相近时总复杂度最低。

具体的做法是,将每个数分解质因数,将质因数划分为两个集合,使这两个集合可以组成的数的个数相近。对于一个,我们将X的贡献加入到中,查询时求的和即可。如果的约数个数为,则存储和访问的时间复杂度都是

这样的问题,比如CTSC2017吉夫特,以及湖南省队集训2018陈江伦的一道题。

对合数取模的技巧

在一类问题中,如利用Burnside引理求本质不同的环染色方案数时


最终要将答案除以n,而只有在模素数意义下这个可以用逆元完成。所以我们可以在计算时对取模,最终将答案除以n即可。

同样,这个技巧可以利用于对合数取模的FWT中

一类数论问题的分析手段

1.约数个数函数

所以约数个数函数的二维前缀和可以这样化简:





以上的玩意都可以求解。如果预处理前,这东西就可以求解了。

如果用线性筛预处理,可以做到

如果有另外一种更快的方法:

可以完成一次计算。

处理1e18大整数除以1e2小整数的问题

由于(long double)的精度无法满足这么大整数除以小整数的除法,因此我们可以采用如下战略:
首先得到大数与小数除法的整数部分,再用大数对小数的模值计算小数部分。

该方法被用在我对'ZJOI2015地震后的幻想乡'一题答案的处理上。

排列计数类DP的处理方式

对于一类Dp问题,通常要求出所有排列的种数。比如3个红球2个绿球组成了排列种数。

可以将每一种排列进行如下的编号(最小表示法):从左往右数球,看见一个未出现过的颜色的球,若之前出现了种颜色,将它编号为。这样,每种排列对应一种最小表示法,因此最终的答案乘上即可。

该方法被用在YZOJ10015yyb和树。

区间求交

在一类为题的解决过程中,我们往往会需要求多个区间的交。有时,区间是会分段的,比如“求一条直线与所有已知直线相交”、或luogu U37667。这时,我们可以求补集的并,这个可以用排序完成统计。

剩余系问题与最短路

在剩余系中的一些问题,如求的倍数中各个位数数字和的最小值,可以使用最短路解决。前面的问题的解答复杂度为。其具体方法是,将剩余系中的每个数看成一个节点,然后节点之间存在转移的边,意为已经有一个剩余系中的某个数,要获得另一个数的代价。然后从初始数字到终止数字的最短路就是答案。

前面的问题可以使用如下的方式建立的图。将一个数字乘,将一个数字加

积性函数线性筛

积性函数的狄利克雷卷积是积性函数。

证明:

因此,积性函数卷积可以线性筛,积性函数点积显然也可以线性筛。

对于线性筛的函数,一般有如下处理方式:

或者可以

一类有决策单调性的Dp

dp有决策单调性,可以用cdq分治将时间复杂度优化为状态数的倍。

用函数定义一个区间的转移区间,状态的转移区间为

则,如果我们将区间的中点的转移点算出来,那么的转移区间的无交并将是,这样,递归二分至多层,每一层的区间即转移区间都恰好构成区间。因此总复杂度将是

如果转移方程中的难以计算,但是可以方便地像莫队一样维护,我们可以在递归的过程中维护,不难发现递归的每一层,左右端点移动的总次数不会超过(大概),因此复杂度不变。

应该不存在更难转移的了。更简单的一般可以用前缀和、线段树等维护。

双栈排序

双栈排序指用两个栈和入栈、出栈两个操作,对一个排列排序。

两个元素不能放入同一个栈当且仅当:存在

1.,dp找出不能在一个栈中的元素对,然后判断是否“不能在同一个栈中”的关系组成二分图。

2.,将二分图模型转成更普适的2-SAT模型,然后用可持久化线段树优化连边过程。空间复杂度 ,时间复杂度 。且空间复杂度常数较大,数据大约需要空间。

3.只求出第一种做法中的二分图的一个生成树进行二分图染色,然后用类似第二种方法的一种方法判断是否可行,即所有可以连的边的两端的点颜色不同。求生成树可以用线段树动态维护某个点左边的所有联通块的编号,每次用线段树取出最大编号和最小编号的可行点,启发式合并它们的联通块。这样的时间复杂度,空间复杂度。且时空复杂度常数都很小,因此整体上优于第二种方法。

4.同第三种方法,但是将所有元素映射到二维平面上,一个点需要与一个左侧无界的矩形区域内的所有点连边。按照一定顺序扫描可以保证这个矩形区域的右侧不降,下方不降,这样线段树就有点多余了,只需要用堆就可以了。

5.还可以使用并查集维护,时间复杂度,见http://uoj.ac/submission/332724

统计平方、方差的手段

方差=平方的平均数-平均数的平方

因此统计方差是建立在统计和、平方和的基础上的。

对于一类问题:如求所有子矩阵内点数的平方和,可以利用这种方法:一个集合内点数的平方和可以表示为一个集合内点对的个数。

统计不包含任何一个点/至少包含一个点的子矩阵个数

设k为点数:

方案:枚举每个“包含至少一个点的子矩阵”包含的最左边的点,然后用更左边的点限制矩阵的上下边界。

SAM中fail树层次遍历序的轻松获得

利用等价类最长长度进行基数排序,得到的就是层次遍历序,因为fail树父亲的长度短于儿子。但是不适用于广义SAM

树上包含根的联通块计数类问题

可以转化为对偶图

如果不要求包含根,可以用点分治

不支持在线的数据结构的在线化

可以采用二进制分组,用个这样的数据结构替代一个大的可在线的数据结构。每当后一个数据结构大于前一个,就暴力合并这两个数据结构。

采用时间线段树可以支持时间区间询问。

有关同余的数学问题的解法

有两种思路,第一种基于复杂度分析,找到问题的分界点,当某个数的权值在上下时分别采用不同的方法。第二种是构造递归结构处理,如BZOJ1421,求的最小

有关图的连通性的题

1.可以枚举1所在的子集(或者枚举一个有代表性的子图,比如竞赛图中拓扑序最小的强连通分量)
2.可以花Bell数的时间枚举划分,然后斯特林容斥

以x为第一关键字,以y为第二关键字的最优化问题

将x乘上很大的权值与y相加,然后做单关键字最优化。

可以类比到凸优化问题。可以处理的最优化问题。

判断一条边在不在XXX内

最大匹配:观察这条边能否通过一个环将自己的流量转移到别的地方。跑完最大匹配后tarjan求出强连通分量,看这条边的两个端点是否在同一个强连通分量内。如果不在,则它必在最大匹配内,反之不一定在。也可以从未匹配点开始DFS,将所有可能通过增广路交换虚实的边打上标记,这些边可以出现在最大匹配内。剩下的边中的匹配边一定出现在最大匹配内。

最大权匹配:删边重做。

最大生成树:它负责连接的两个联通块间是否有同样权值的边。

查询被两个东西同时覆盖的元素个数:同时维护,如CF-gym-102028

枚举拆分法

一些题目可能可以通过暴力枚举整数的拆分解决,其特征是对称性较强、等价情况较多、数据范围左右。

如:BZOJ4671异或图,BZOJ1478Isomorphism

动态线性基:

方法1:时间线段树(需要离线),复杂度带一个

方法2:维护线性基的每个基向量是由哪几个原始向量组成的,每次删除一个向量,找出这个向量参与的基向量中最高位最低的一个,用这个向量异或其它被删除向量参与的基向量。现在其余的向量里都没有了这个被删除向量,并且线性基结构没有发生改变。现在只需要修改最高位最低的那个向量,再将其重新插入线性基即可。复杂度没有

状态压缩DP减少空间复杂度的技巧

由于一般的状态压缩dp的转移是一个分层图,只会存在相邻层之间的转移,我们不妨将每一层的状态拿出来单独保存,这样可以将空间复杂度从降低至

tarjan对拓扑排序的替代

tarjan后的编号即为拓扑序的反序。

形态不变的树的一个转化技巧

如SPOJ-QTREE6,可以将自己连向父亲的边的颜色设为自己的颜色。

用SAM可以构建后缀数组

实测在随机数据下比基数排序树状数组慢一倍左右,但是卡树状数组数据下性能稳定,可以比树状数组快至少

计数类问题的一些技巧

一个有意思的对偶

将一个数拆分为个数的方案,等价于拆分成不超过的数。这两个问题可以用五边形数或者Dp求解。其中Dp方法为分块Dp。

另外,将一个数拆分为不超过的不重复数字,等价于拆分成不超过个连续数字。这两个问题可以用Dp求解。方法是:表示拆分为不超过的不重复数字,前个数和为的方案数,其中,支持两个转移:将所有数加,或者将所有数加,再最前面添。注意当最后一个数大于的时候需要容斥掉这个情况,即

在线段树上一个区间内二分

一般的线段树上二分只针对整个线段树区间。如果需要在一个特定的区间内二分,找到“最靠右的满足某条件的值”,可以先像普通询问一样找到log个可能区间,在最右边的一个可能区间内二分。时间复杂度依旧是

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注