总结

文章目录

  • 一、常见错误
    • 代码细节
    • 其它
  • 二、一些技巧
    • 一、动态规划
      • DP设计
      • DP优化
    • 二、字符串
    • 三、数学
      • 数论等
      • 计数
    • 四、博弈
    • 五、树上问题
    • 六、图论
    • 七、网络流
    • 八、数据结构
    • 九、其它
  • 三、一些公式
    • 组合数
    • 二项式反演
    • min/max容斥扩展
    • 单位根反演
    • EXCRT
    • 杜教筛
  • 四、一些模板

一、常见错误

代码细节

  1. 当两个特别大的数相乘后取模时,要使用快速乘。
  2. 注意:使用long long时,要检查传参是否传int。
  3. 注意:不要3数连乘 不要int×int 不要忘记负数 不要忘模 (long long范围)。
  4. 数组大小,乘2,乘4等问题。
  5. 数组类型,连续定义有时会错(如把int定义成bool)。
  6. 不要忘记初始化。
  7. 多测清空问题:每定义一个变量,数组,就要添加对应的清空代码。
  8. 行,列一定要看清楚,不要写混。
  9. 除法,逆元可能被卡(虽然模数是质数,但除数模完后,可能是0)。要尽量避免求逆元。对于上述情况,可以定义一个结构体维护,额外记录下0的个数。

其它

  1. 2-SAT:a->b即!b->!a 两条边都要连。

  2. splay,lct注意事项:

    1. 旋转时fa的连接。

    2. 旋转时新根的fa。

    3. 旋转时旧根的fa的儿子要赋值为新根。

    4. 维护标记:

      旋转后,2次上传
      splay前,下放标记
      access更换重儿子时,上传
      cut后,上传(也许)

  3. 行列式,高斯消元等的乘法值要预处理出来,否则会错,就是

    int t=1ll*sz[j][i]*ksm(sz[i][i],md-2)%md;
    
  4. 线段树合并,主席树的空间要略大于 O ( n l o g n ) O(nlogn) O(nlogn)

  5. 线段树合并要特判叶子节点。

  6. a/b 向上取整 (a+b-1)/b 要特判负数。

  7. 注意输出-0.00的问题。

  8. 使用标记永久化,上传时要注意考虑标记。

  9. 主席树在新建节点时要把原来的复制全。

  10. 点在多边形内部判定,需要考虑点在边界上的情况。

二、一些技巧

一、动态规划

DP设计

  1. 互质数的选择,当质因数较大时采用分组背包,较小时状压记录,以平方根为界(luogu寿司晚宴)。

  2. 对于状态转移有环的动态规划,使用如下方式求解:

    1. 若转移里没有max/min,只有加减乘除运算,可以建立方程组,通过高斯消元在O(n^3)得出解。

    2. 若转移只是对一些元素取max/min(或第k大/小值),再加/减一个值,可以建图,使用dij或spfa求解。

    3. 若u依赖v,且若v能更新u,则dp[u]>=dp[v],且求的是min(例如dp(u)=min(S(u)+∑dp[v],K(u)))。

      每次:

      1:把值最小的点标记
      2:if 一个点u依赖的所有v都被标记
      计算dp(u),并标记u,
      3:回到1
      直到所有点被标记。
      就结束。

    4. 若2,3都不满足,可以使用如下方法:
      先把每个点都入队,每次从队首取出u,并计算dp(u),若dp(u)发生更新,则把依赖u的v都加入队列(前提是v不在队列中)。
      这个方法很像spfa,但时间复杂度最坏为O(nm)。

    例题:模拟赛tiger,luogu 骑士游戏

  3. 每次找一段删除,删除后自动合并的DP的一种解决方法:

    先设dp(i,j)表示考虑i~j的结果
    然后,考虑i这个点的情况:i一定是和之后的几段一起合并后删除
    例如:i j
    -----*****–@@-&&&&&--------
    而中间的几段一定不能一起删除

    因此,像
    ------####------#####-------

    这种情况是不可能的
    所以,中间的段就是单独的子问题了。
    还要加一组状态k,表示之前的连续段的信息。

  4. 对于树形背包,通常,我们有两种方法:

    基于dfs合并,可以知道每个点的确切情况(比如子树中选了多少),但是,复杂度较高(因为合并背包很慢)。
    在dfs序上DP,复杂度较低,但是,不能 知道每个点的确切情况(因为是在序列上)。

  5. 有字典序限制的DP(以大于为例):

    通常,我们有两种方法:
    1、记录下当前是“放什么都大于(之前已经大于)”,还是“放大于的才大于”。
    这个通常用于数位dp
    但是,这个会使数有本质区别,难以处理。
    2、确定一个前缀一模一样,下一位大于,之后随意。
    这样我们可以根据前缀算出当前状态,并且之后的dp不受影响。
    但是,有时这样复杂度较高,并且,如果有多个有字典序限制,这个方法就不适用了。

  6. 若在树上进行类似“随机游走的”DP,可以不用高斯消元:

    1、设 f u f_u fu= k u ∗ f f a + b u k_u*f_{fa}+b_u kuffa+bu
    2、由于u的计算仅依赖u的父亲和儿子,把儿子用“1”的方法表示,儿子的父亲是本身,这样u就只依赖u和父亲。
    3、通过移项,使u仅依赖父亲,进而表示为“1”中的形式。
    4、最后,根节点的b就是答案。

  7. 有些容斥原理可以DP,在奇偶性改变时,取相反数即可。

  8. 关于连通图计数的DP,可以设 d p i dp_i dpi表示 i i i个点连通的方案数,转移时先算出方案总数,再减去不连通的。不连通的可以枚举编号最小的点所在连通块的点数 j j j,用 d p i dp_i dpi减去 d p j ∗ x dp_j*x dpjx,其中 x x x是剩余的 i − j i-j ij个点的方案总数。当图相对复杂(比如完全二分图)时,可以增加一维状态,同时也要多一层枚举。

  9. 树形的有依赖dp也可以分两次dp,分别考虑向上和向下传递(即换根dp)。

  10. dp套dp:通过外层dp,算出使某个问题的解为给定值的方案数等,其中这个问题需要用内层dp解决。
    按照内层dp所需要的输入顺序进行外层dp,将内层dp转移所需要的依赖(可以观察填表的过程)通过状压记录成状态(可以预处理出这些状态的转移自动机),进行dp。

  11. 数位DP可以统计L到R之间的 f ( x ) f(x) f(x)之和。要求f只和x的数位有关。若f本身就是递推式,可以按照f的递推顺序进行数位DP,并设F表示在限制下f之和,并把f的转移式子改成F的。

  12. 有时,可以用两种方式表达计数DP,根据这两种表示法相等的,来列方程求解。

  13. 秩为x的矩阵计数可以DP,只要记录当前的秩,转移时考虑是否增加秩即可。

  14. 麻将题(横三竖三的),可以dp,因为三个横的就能变成三个竖的,因此横的不会超过2个,可以压进dp状态。具体细节

  15. 区间DP通常有两种转移形式:从两端删除,或从中间分裂。

  16. 涉及到矩形计数,最大矩形等问题时,可以考虑悬线法,配合单调栈,枚举一个数作为区间的最小值。

DP优化

  1. 对于有些DP,可以先设计出比较简单的状态后,去掉一些无用状态,等价状态,进行优化。(可以使用记忆化搜索)
  2. 带权二分:若选择dp等满足凸性,可以用直线切割凸包,将选的代价修改,通过二分找到合适的位置。
  3. 在对DP进行卡常优化时,可以试试改成记忆化搜索,有时能快很多。
  4. 如果转移时需要枚举相交的区间,可以使用容斥,转化为枚举不相交的区间,这样l和r就没有联系了,可以分开计算,从而减少一维。
  5. 决策单调性总结
  6. 对于一般的01背包,复杂度为 O ( N M ) O(NM) O(NM),但如果物品体积的范围C不算大,可以按照C去依次计算,当考虑到体积为x的物品时,对于模x意义下相等的数,从小到大满足决策单调性,使用分治,可以把复杂度降低至 O ( N l o g N + M C l o g M ) O(NlogN+MClogM) O(NlogN+MClogM)
    void dfs(int l,int r,int l2,int r2,int x,int s)
    {
    	if(l>r)return;
    	int m=(l+r)>>1,wz=0;
    	for(int y=l2;y<=r2&&y<=m;y++)
    	{
    		if(m-y>=su[x].size())
    			continue;
    		ll t=dp[x*y+s]+su[x][m-y];
    		if(t>jh[x*m+s])
    			jh[x*m+s]=t,wz=y;
    	}
    	dfs(l,m-1,l2,wz,x,s);dfs(m+1,r,wz,r2,x,s);
    }
    
    for(int i=1;i<=300;i++)
    {
    	for(int s=0;s<i;s++)
    	{
    		int t=(k-s)/i;
    		dfs(0,t,0,t,i,s);
    	}
    	for(int x=1;x<=k;x++)
    	{
    		dp[x]=jh[x];
    		jh[x]=0;
    	}
    }
    
  7. 在dp转移时如果需要卷积,但是和他卷积的数组差分后容易维护(比如 d p ( i ) = Σ d p ( i − j ) ∗ j dp(i)=\Sigma dp(i-j)*j dp(i)=Σdp(ij)j),可以不用NTT,直接用前缀和维护。

二、字符串

  1. AC自动机算法在匹配时,在fail树上的链将都被匹配,所以经常与树上算法联合使用(luogu P3796,luogu P2336喵星球上的点名)。
  2. 后缀自动机跳fail的注意事项 (压缩字符串)。
  3. 处理类似重复子串的问题,可以考虑枚举长度L,然后每隔L放一个关键点,并计算相邻的关键点的lcp,lcs等信息。
  4. 扩展后缀数组:在trie树上构建,使用倍增,类似普通SA。
  5. 处理回文的问题时,可以枚举回文中心。为了减少细节,有时可以在每个字符后面加入KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲
  6. 回文自动机(PAM)
  7. 前后添加的哈希,可以从中间开始,维护左边的后缀,右面的前缀的(从左到右的)哈希值。询问子串时拼接即可。或者维护懒标记。

三、数学

数论等

  1. O ( 1 ) O(1) O(1)快速乘代码:

    ll ksc(ll a,ll b)
    {
    	return (a*b-(ll)((long double)a/md*b)*md+md)%md; 
    }
    
  2. 中国剩余定理,可以在计算时之算出前缀积(变算边乘),复杂度可以从 O ( n 2 ) O(n^2) O(n2)降到 O ( n ) O(n) O(n),在计算时调整下余数即可。如果需要高精维护,此法可以起到优化。

  3. 1到n中所有数的约数的约数个数和大概是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的。

  4. 带变量x的组合数,可以把组合数拆开变成下降幂,用斯特林数+二项式定理表示出系数,从而转化为关于x的多项式。

  5. 如果对实数的精度要求非常高,且运算不复杂,可以定义分数类解决。

  6. BSGS算法在有多组询问时,可以适当增加步长。

计数

  1. 遇到用若干个数相加组合成一个数的问题,可以考虑同余最短路(NOIP2017 小凯的疑惑,墨墨的等式)。

  2. 平方和问题:考虑平方的实际意义 (二维枚举,二维dp)。带组合数的,也可以用类似做法。

  3. 倍增合并(有时可以使用fft加速合并),有时能比矩阵快速幂少一个m。

  4. 矩阵乘法两种形式:

    1. 数列递推,每次生成一个。
    2. 高维递推优化,每次推进一层。
  5. 枚举,算贡献(相当于交换求和符号)。

  6. 两种容斥原理:交集,并集。(都要考虑到)

  7. 关于匹配的容斥:若有被多次匹配的,就有没被匹配的,按照没被匹配的进行容斥。

  8. 有些计数类的递推(如: d p ( i , j ) = d p ( i , j − 1 ) + d p ( i + 1 , j + 1 ) dp(i,j)=dp(i,j-1)+dp(i+1,j+1) dp(i,j)=dp(i,j1)+dp(i+1,j+1)),可以画出转移的图后,(通过扭转等)转移为网格图路径计数问题。

  9. 网格图有限制的路径计数:

    1. 只有一个限制(如三角形,j<=i),可以容斥,不合法的可以将起点或终点进行对称。
    2. 有两个限制(如平行四边形中),还是容斥,但要考虑反复经过分界线的情况。如:A,ABA,B,BAB等应减去。AB,BA,ABAB,BABA以及不进行对称等应加上。
    int ans=F(n,n+m);
    int a=0,b=0;A(a,b);B(a,b);
    ans=(ans+jisuan1(a,b))%md;
    a=0;b=0;B(a,b);A(a,b);
    ans=(ans+jisuan2(a,b))%md;
    a=0;b=0;B(a,b);
    ans=(ans-jisuan1(a,b)+md)%md;
    a=0;b=0;A(a,b);
    ans=(ans-jisuan2(a,b)+md)%md;
    
  10. 网格图中两条不相交路径计数,可以用总数减去相交的,相交的就是把交点后面的路径交换。

  11. 多项式可以放进矩阵中,进行矩阵快速幂。

  12. 如果多项式的次数始终不是很多,也可以直接用点值法维护,在开始和结束时进行NTT即可。

  13. FFT为了防止炸精,可以预处理出单位根,而不是直接乘。

    for(int i=0;i<(h>>1);i++)
    	ww[i]=cp(cos(2*pi*lx*i/h),sin(2*pi*lx*i/h));
    
  14. 在FFT时若有 i k ik ik之类的项,可以转化为 k ( k + 1 ) 2 + i ( i − 1 ) 2 − ( k − i ) ( k − i + 1 ) 2 \frac{k(k+1)}{2}+\frac{i(i-1)}{2}-\frac{(k-i)(k-i+1)}{2} 2k(k+1)+2i(i1)2(ki)(ki+1),便于FFT。

  15. 若要求 Σ A i , j × x i × x j \Sigma A_{i,j}×x_{i}×x{j} ΣAi,j×xi×xj的最值,可以对于每一个x,只看关于它的项,发现是一个二次函数,那么让这个x等于对称轴即可。这样会得出n条方程,高消即可。

四、博弈

  1. 只要两个子游戏没有关联了,就可以用sg异或了。
  2. 翻硬币游戏,只要选的硬币是反转的硬币中编号最大的即可。计算时可以把直接翻转理解为添加。
  3. 阶梯nim:每次取走若干个放到上一堆中。只要记录奇数位置的异或即可。这个游戏也可以放到树上进行,方法相同。

五、树上问题

  1. 在一棵树上统计子树信息,如果子树信息不能快速合并,可以采用dsu on tree算法(luogu 雨天的尾巴,回文路径计数)。

  2. 树链合并(深度和减去相邻lca深度),暴力树链合并,access是均摊的。

  3. 基环树方法:将环提出,变为森林,对每棵树计算,然后合并。

  4. 树统计(效率):线段树合并>启发式合并=dsu on tree

  5. m个节点的所有lca在O(m)级别,可以用虚树。

  6. 给定树上若干的点,求最小生成树大小,可以按照dfs序排序后,用总深度减去相邻的lca的深度,再减去所有的lca的深度。

  7. 建虚树的步骤

    int build(vector<int> &sz)
    {
    	//1. 将点按照dfs序为关键字排序(此处已省略)。
    	for(int i=0;i<cs;i++)
    		ve[cl[i]].clear();
    	cl[0]=st[0]=sz[0];cs=tp=1;//2. 清空数组,将第1个点压到栈中。
    	for(int i=1;i<sz.size();i++)
    	{
    		int u=sz[i],lc=getlca(u,st[tp-1]);//3. 枚举到下一个点u,计算u与栈顶点的最近公共祖先lca
    		while(tp>=2&&de[lc]<=de[st[tp-2]])//4. 假设栈中栈顶下方的点为w(若栈中只有1个点就直跳过这一步),若w点的深度大于等于lca就把v向w连一条边,并且弹掉v,重复此步,否则就到下一步。
    		{
    			ve[st[tp-2]].push_back(st[tp-1]);
    			tp-=1;lc=getlca(u,st[tp-1]);
    		}
    		if(lc!=st[tp-1])//5. 若lca不是当前的栈顶点,那么就把lca和栈顶点连边
    		{
    			ve[lc].push_back(st[tp-1]);
    			st[tp-1]=cl[cs++]=lc;//并把栈顶变为lca
    		}
    		st[tp++]=cl[cs++]=u;//6. 最后把u压入栈中
    	}
    	for(int i=0;i+1<tp;i++)//7. 把栈顶v与栈顶下方的点为w连边,并且把v弹掉,这么做直到栈里只有一个点
    		ve[st[i]].push_back(st[i+1]);
    	return st[0];//这个点就是虚树的根了
    	//为了方便,有时把1也加进去。
    }
    
  8. 长链剖分优化dp时,对于轻儿子直接爆算,重儿子继承后将指针移位,进行+1或-1的调整。比如:

    int& F(int u,int i)
    {
    	if(i>=dep[u])
    		return z0;
    	return f[lf[u]+i];
    }
    if(son[u]!=-1)
    {
    	dfs2(son[u],u);
    	lg[u]=lg[son[u]]+1;//指针移动
    	lf[u]=lf[son[u]]-1;//指针移动
    }
    F(u,0)=1;
    if(son[u]==-1)return;
    for(int i=fr[u];i!=-1;i=ne[i])
    {
    	if(v[i]==fu||v[i]==son[u])
    		continue;
    //进行转移
    }
    
  9. 若要求一段路径上的颜色数,可以考虑每种颜色的贡献:若删除所有这种颜色后,起点和终点不在一个连通块中,则有贡献。

  10. 树的重心,也是树上到其他点距离之和最小的点,满足其子树大小的最大值最小(不超过一半)。如果有两个重心,则这两个重心连着,且最大子树正好为一半。

  11. 距离树上某一个点的最远点一定是(任意)直径的一个端点。

  12. 合并两棵树的直径时,新树的直径端点一定是这两棵树的直径端点(共四个)中的两个。可以分6种情况讨论。

六、图论

  1. 最小生成树更新:只需判断环的最值即可

  2. 图上的所有MST,满足:

    1、对于任意权值的边,所有最小生成树中这个权值的边的数量是一定的(根据克鲁斯卡尔算法即可得出)
    2、对于任意正确加边方案,加完小于某权值的所有边后图的连通性是一样的
    3、对于相同权值的边,任意顺序都是可以的。

  3. 边权只有0,1的图的最短路可以用bfs求解。

  4. 在涉及到连通性,以及最长路等问题要考虑缩点(受欢迎的牛)。

  5. 欧拉回路构造:出栈时输出+“当前弧优化”

  6. 三元环枚举:按度数将边定向,大->小,之后暴力枚举即可。

  7. 2-SAT:若a能推出b,连a->b,连!b->!a。

    之后,找scc,若一个变量的两种取值位于同一scc,则无解。
    若要构造解,则取两种取值中scc编号较小的。

  8. 奇环的判断使用黑白染色。

  9. 给一个图,删边,问连通性,可以把非树边随机一个权值,树边的权值为覆盖它的非树边的权值的异或和。查询时求出是否有异或和为0的子集即可。

七、网络流

  1. 平面图的最小割就是对偶图的最短路(每个面看做一个点,相邻的面看做边)(luogu P2046海拔,luogu 狼抓兔子)。

  2. 网络流的最小割模型:

    1. “文理分科”
    2. “选择+2个具有贡献”
  3. 在题目涉及到所有点对的最大流/最小割时,可以通过最小割树,将其转化为树链上的最小值。

  4. 对于二分图完美匹配的判定,可以考虑Hall定理。

  5. 问题:有n个点,选择每个点有代价,m条关系(x,y,z)表示同时选择x,y有z的收益。使收益-代价最小。

    可以使用最小割求解。
    首先,假设可以得到所有收益,不付出代价。并用最小割割掉不合法的。
    S向每个点连边,边权为代价。每个点向T连边,边权为包括它的收益和的一半。
    对于每条关系, x x x y y y连边权为 z 2 \frac{z}{2} 2z双向边。
    可以把边权先扩大到2倍。

  6. 费用流处理负圈的方法

  7. 最小割的数学定义,令 x x x为一个01变量,我们假设若 x x x最后在 S S S集合,则 x = 0 x=0 x=0,否则 x = 1 x=1 x=1
    则边 ( S , x , a ) (S,x,a) (S,x,a)对答案的贡献为 a x ax ax
    则边 ( x , T , a ) (x,T,a) (x,T,a)对答案的贡献为 a ( 1 − x ) a(1-x) a(1x)
    则边 ( x , y , a ) (x,y,a) (x,y,a)对答案的贡献为 a ( 1 − x ) y a(1-x)y a(1x)y
    同理,如果我们能把贡献写为以上的形式,则可以用最小割求最优解
    比如:有若干个01变量,每个变量取0/1有代价,还有若干关系(x,y,z)表示如果x取1,y取0,有z的代价。要求总代价最小。可以使用最小割。

八、数据结构

  1. 有时,权值线段树可以代替treap,能减小常数和代码量(luogu 郁闷的出纳员)。

  2. 有时,线段树可以代替splay,方法时记录每个位置是否有元素,查询第k个值时线段树上二分(NOIP2017 列队)。

  3. 处理区间内出现过的数时,可以求出每个数的后继,然后就变成了区间内大于某数的数(luogu HH的项链)。

  4. 某种在线–>离线:建立线段树/树状数组,当某个区间都被加入时,离线处理,要求询问区间分裂后能合并结果(其实这个叫做二进制分组)。

  5. 很多树形数据结构都可以打标记(线段树,splay,左偏树)。

  6. 通常的树持久化方法:每次新建一个根。避免持久化的技巧:版本树(离线)。

  7. 在处理覆盖问题时,可以用并查集维护每个点向后第一个未被覆盖的位置(包括自身),比线段树快,树上覆盖也可以。

  8. 笛卡尔树:将RMQ变为lca,可以动态维护单调栈,解决RMQ之和等问题。

  9. 计算最大矩形时,除了单调栈,还可以递归,就是每次找到最小值,并递归它两边的区域。

  10. 处理异或的问题有一种常用技巧:就是把每个二进制位拆开单独处理,这样只有不同的才会有贡献。可以将异或问题转换为了是否为不同的数。

  11. 有时,我们的可持久化并查集只需要查询历史版本,不需要在历史版本上修改(例如添加边后,询问之前的连通性)。

    这时,我们有一个log的做法。
    维护一个只按秩合并,不路径压缩的并查集。
    对于每条边,记录它出现的时间,判断连通性找根时,如果它到它的父节点的边出现了,就找父节点的根,否则它就是根。
    这样,不路径压缩,边的状态只有两种,存在/不存在,若存在就是唯一的。

  12. 线段树合并,通常用在树上。

    需要满足的条件:在合并时,对于一棵空树,可以直接return或在另一棵树上打标记。

  13. 看到RMQ之和的问题,可以考虑单调栈,或者每次找RMQ并分治两边。

  14. 只进行加的高精,有时可以使用线段树维护:加法时,在线段树上二分进行进位。比较时,使用哈希找lcp进行比较(要注意从高位到低位)。并且,可以可持久化。

  15. 二维线段树通常使用动态开点,x树的每个节点都保存它的y树的根。

  16. 主席树,二维线段树等数据结构如果要区间修改,通常使用标记永久化(要求标记之间没有顺序要求)。

  17. trie字典树支持删除,只要额外维护子树大小即可。

  18. 标记永久化可以解决一些其它问题,比如:有一棵树,单点修改,查询一个点到根的路径上与x异或的最大值。这个可以做到 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)时间, O ( n l o g n ) O(nlogn) O(nlogn)空间。

  19. 启发式合并,启发式分裂,有时可以做到log。

九、其它

  1. 生成匹配的括号序列,只需模拟一个栈,记录栈中的括号数即可(CF1015F,CF508E)。

  2. 求第k小值(k不大),可以采用A*算法,要保证每种状态都能被扩展出,且扩展出的状态非递减(luogu 超级钢琴,K短路,OVOO)。

  3. 离线处理以及强制在线:

    1. 离线处理可以把询问排序,并按照顺序求解。强制在线可以全部处理后可持久化(NOI2018 归程)。
    2. 离线处理可以倒推。
    3. 离线处理可以得出每种操作的时间序(loj121 动态图的连通性)。
  4. 当处理一个要求整体满足的问题时,可以只考虑局部,当所有的局部都满足时,整体就满足了(luogu 双栈排序,NOI2018 冒泡排序)。

  5. meet-in-the-middle 算法

    应用:BSGS优化,分别以1,平方根为步长,进行预处理,这样,任意一个数都能用处理好的两个数之和来表示。

  6. 第k小问题的计数可以用容斥 (秘密袭击),就是说:

    x成为第k小需要满足两个条件: < x <x的数目小于k, ≤ x \leq x x的数目大于等于k。
    可以用满足1的减去满足1且不满足2的。
    显然,不满足2,就一定满足1。
    因此,用“ < x <x的数目小于k”减去“ ≤ x \leq x x的数目小于k”即可。

  7. 模拟网络流(数据结构实现增广),堆模拟反悔操作

  8. 分类考虑:一部分满足种类数小(预处理),一部分计算快,可以分类求(通常平方根为界,但实际上有偏差)。

  9. 易忘的:

    线段树合并
    分块,分类
    差分约束
    预处理
    线段树分治,线段树建图,线段树扫描线
    2-SAT
    bitset优化

  10. 传有数组的结构体要尽量用引用。

  11. 模数要用define或const!!!!会快些

  12. 在枚举子集时,若要遍历其中的元素,可以采用如下方法,会快些:

    for(int i=0,j=1;i<n;i++,j=(j<<1))
        lg[j]=i;
    //……
    for(int i=0;i<(1<<n);i++)
    {
        for(int t=i;t>0;t^=t&(-t))
        {
            int k=lg[t&(-t)];
            //……
        }
    }
    
  13. 看到方差可以考虑推式子,转化为平方和-平均数×总和。

  14. 排列的三维偏序,可以容斥。答案为 F ( a , b ) + F ( a , c ) + F ( b , c ) − n ∗ ( n − 1 ) 2 2 \frac{F(a,b)+F(a,c)+F(b,c)-\frac{n*(n-1)}{2}}{2} 2F(a,b)+F(a,c)+F(b,c)2n(n1)。F为二位偏序。

  15. 判断两种状态能否到达时,若状态的转移有单方向性,可以求出两个状态按照方向转以后到达的最终状态,判断是否相等。(其实就是状态的转移是树形的)。

  16. FWT的一个小技巧:此处

  17. 正难则反,可以把问题反过来(比如:让求字符串中是否有不相等的距离为k的两个位置,由于字符串算法大多是求匹配的,所以可以反过来,看是否都相等)。

  18. Hash挂链通常比map快,快很多,但要注意hash的方法。

三、一些公式

组合数

总结_第1张图片

二项式反演

总结_第2张图片

min/max容斥扩展

单位根反演

EXCRT

x = r 1 ( m o d a 1 ) x = r1 (mod a1) x=r1(moda1)
x = r 2 ( m o d a 2 ) x = r2 (mod a2) x=r2(moda2)
k 1 ∗ a 1 + r 1 = k 2 ∗ a 2 + r 2 k1 * a1 + r1 = k2 * a2 + r2 k1a1+r1=k2a2+r2
k 1 ∗ a 1 = ( r 2 – r 1 ) ( m o d a 2 ) ( 3 ) k1 * a1 = (r2 – r1) (mod a2) (3) k1a1=(r2r1)(moda2)3
在此基础上我们求k1这个未知变量即形如 a x = c ( m o d b ) ax = c (mod b) ax=c(modb)中的x的解,
设得出的解为k0(最小正整数解), 那么(3)的 k1的通解一定为
k 1 = k 0 + t ∗ a 2 g c d ( a 1 , a 2 ) k1 = k0 + t * \frac{a2}{gcd(a1, a2)} k1=k0+tgcd(a1,a2)a2 (t为任意正整数)
带入(1)中即可得 x = k 0 ∗ a 1 + r 1 + t ∗ a 1 ∗ a 2 g c d ( a 1 , a 2 ) x = k0 * a1 + r1 + t * a1 * \frac{a2}{gcd(a1, a2)} x=k0a1+r1+ta1gcd(a1,a2)a2
x = k 0 ∗ a 1 + r 1 ( m o d ( l c m ( a 1 , a 2 ) ) ) ( 4 ) x = k0 * a1 + r1 (mod (lcm(a1, a2))) (4) x=k0a1+r1(mod(lcm(a1,a2)))(4)
(4)就是我们想要的合并方程(切记k0为最小正整数解)

杜教筛

S ( n ) = Σ ( f ∗ g ) ( i ) − Σ g ( d ) ∗ S ( n d ) S(n)=\Sigma(f*g)(i)-\Sigma g(d)*S(\frac{n}{d}) S(n)=Σ(fg)(i)Σg(d)S(dn)

四、一些模板

由于篇幅太长,见此处

你可能感兴趣的:(总结)