学习知识点发现的各种小tip

FFT,NTT

  1. 多项式的点值表示法是可以直接进行+,-,*,/等运算的,考虑其定义即可。但一般加减运算都是直接系数相加减,可以少做几次NTT/FFT,降低代码时间复杂度。(甚至有可能掩饰一些代码上的错误,比如有个数组进行了一次NTT+逆NTT后%xn,将大于xn的部分系数清零了,但没有影响你所需的(即n以内的)这部分系数的大小。此时再将这个长度已缩小到n的系数序列按第一次的序列长len=n<<1来做NTT就会产生错误的点值数组,但如果直接加减则没有问题);

  2. 进行FFT/NTT时,一定记得FFT/NTT与逆FFT/NTT所用的len相同。而且在一次FFT/NTT+逆FFT/NTT的过程没完成前不能更改存储点值/系数的数组,哪怕是超过当前所求长度n的部分改变,也会影响n以内的数据的正误;

  3. 进行多项式开方,求逆,ln,exp等运算时,如有不同的函数都要用中间数组,就多开几个不同的,别图省事。这样可以防止运行过程中仍需使用的数据被其他函数覆盖,也可以少清几个零。不过在不需卡常时,还是每次使用都把数组清空一遍,该赋初值的就赋初值。因为有些看着为零的变量/没被使用过的数组,不知道什么时候就被调用过了,多项式那么多函数套在一起,检查起来也费劲,还不如多写几行代码。

  4. 像赋初值,求len,求反转数组rev[i],多项式相乘的使用频率高的都写成函数,易读又好查错。

像上面那些显而易见的细节只有我这种蒟蒻才会需要吧……学多项式和生成函数感觉自己快退役了5555~

莫比乌斯反演

莫比乌斯反演中常见的做法有:

  1. \([gcd(x,y)==1]\)\(\varepsilon(gcd(x,y))\)化为\(\Sigma_{p|gcd(x,y)}\mu(p)\)
  2. 改变枚举顺序,将枚举因子提前,然后通常可以运用整除分块:\(\Sigma_{i=1}^n\Sigma_{j=1}^m\Sigma_{p|gcd(x,y)}\mu(p)\Rightarrow \Sigma_{p=1}^{min(n,m)}\mu(p)\Sigma_{i=1}^{n/p}\Sigma_{j=1}^{m/p}1\Rightarrow \Sigma_{p=1}^{min(n,m)}\mu(p)\lfloor \frac np \rfloor \lfloor \frac mp \rfloor\)
  3. 如果有类似\(\Sigma_{i=1}^nf(i)\Sigma_{j=1}^{n/i}g(j)h(ij)\)的表达,可以尝试转化为\(\Sigma_{t=1}^nh(t)\Sigma_{p|t}f(p)g(\frac tp)\),然后用数据结构维护\(\Sigma_{p|t}f(p)g(\frac tp)\)

扩展中国剩余定理

  1. 中国剩余定理似乎可以用扩展中国剩余定理完全代替哦 废话,本来就是扩展嘛
  2. 扩展中国剩余定理有两种实现方法:联立和代入。现有两个方程:\(x \equiv a \pmod {p_1},x \equiv b \pmod {p_2}\)

联立:\(x=a+t_1*p_1=b+t_2*p_2\),故 \(t_1*p_1-t_2*p_2=b-a\)。扩展欧拉定理得解。

代入:\(x \equiv a \pmod {p_1}\) 得出 \(x+t_1*p_1=a\)。求出特解\(x=ans\),可得通解\(x=ans+t_1*p_1\)。又有\(x \equiv b \pmod {p_2}\),即\(ans+t_1*p_1+t_2*p_2=b\) 移项得\(t_1*p_1+t_2*p_2=b-ans\),扩展欧拉定理得解。

当公式有变形时,似乎代入法能更简洁地得出解法:有 \(n\)个形如 \(w_ix\equiv a_i \pmod {p_i}\)的同余方程,已得出前 \(n-1\)个的解 \(ans\)和模数 \(P\),第 \(n\)个方程为 \(wx \equiv a \pmod p\),代入可得 \(w(ans+tP)+t_1p=a\),移项得 \(wPt+t_1p=a-w*ans\)。求出特解 \(t=T\),则通解 \(t=T+\frac{p}{gcd(p,wP)}\),则前 \(n\)个方程的解为 \(ans_n=ans_{n-1}+TP\),通解为 \(ans_{n-1}+(T+\frac{p}{gcd(p,wP)})*P\),即 \(ans_n+\frac{pP}{gcd(p,wP)}\),前 \(n\)个方程的模数为 \(\frac{pP}{gcd(p,wP)}\)。例题详见[NOI2018] 屠龙勇士做法。

路径计数问题

翻折法:求点A(0,0)到B(n,m),不经过某一条直线的不降路径条数(即只能向上或向右的路径),可作A关于直线的对称点A',答案即为A到B的不降路径条数-A'到B的不降路径条数。
证明:对于一条从A出发经过直线的路径,将其与直线最后的交点前的部分沿直线翻折,即可转化为一条从A'到B的路径。详见OI-wiki卡特兰数部分。

树形背包

有一类树形背包f[i][j],第一维表示点的编号,第二维表示子树内选的点数且限制不超过m。有如下转移:

void dfs(int u,int last) {
	sz[u]=1;
	for(int k=head[u];k;k=e[k].next)
	{
		int v=e[k].to; if(v==last) continue;
		dfs(v,u);
		for(int j=0;j<=min(m,sz[u]);++j)
			for(int k=0;k<=min(m-j,sz[v]);++k)
				dp[u][j+k]=dp[u][j] * dp[v][k];
	}
} 

其总复杂度可证为\(O(nm)\)。具体见这篇博客(我没看懂,但我大受震撼)。

取模情况下大数组合数的计算

\({n \choose m}\%p\)问题中若\(n\)\(m\)均较大,当然是使用卢卡斯定理;若只是\(m\)较大,答案为0;若只是\(n\)较大呢?
在一类特殊情况下可以直接对\(n\)取模:\({n\choose m}*(m!)\)
难道不存在一种可能:\(n\)取模后变得小于\(m\),导致答案变成0,不同于原来了?实际上这是没关系的:

如果 \(n\ \%mod\) 后变得小于\(m\),那么证明 \(n-val<m\) ( \(val\) 为小于 \(n\) 的最大的\(mod\) 的倍数)。那么原式即 \(n(n-1)(n-2)……(n-m+1)\)。这个算式中一定有一个 \(mod\) 的倍数,所以答案就是0。

如果 \(n\ \%mod\) 后仍大于\(m\),那么将 \(n(n-1)……(n-m+1)\)的每一项 \(\% mod\) ,结果肯定仍相同。

posted @ 2021-02-02 17:16  caocao11  阅读(137)  评论(2编辑  收藏  举报