22暑期ACM集训周记05
一、第五周学习总结表
Contest | 完成情况 |
---|---|
数论1(快速幂、GCDLCM、扩欧) | 6/16 |
数论2(同余与逆元、素数、抽屉原理) | 8/14 |
组合数学1(杨辉三角、容斥原理、母函数) | 5/8 |
组合数学2(卡特兰数、stirling数) | 6/11 |
背包问题 | 11/18 |
Atcoder比赛+补题 | 6/10 |
郑州大学比赛+补题 | 6/12 |
小白月赛比赛+补题 | 3/5 |
总计 | 51/64 |
个人感悟
本周开始感觉有点力不从心,首先是每天的题目越来越多,但是学的也越来越难,主要是对数学知识理解的不够深刻,并且原本数学不怎么好。在学习数论的时候往往要很久才能理解,理解后也不能很快的应用在题目上面。另外每天题目很多,这周没来得及做的题目很多,却也没有什么时间去补题和写题解。有些灵感涌现时没有及时记录在题解内,很可惜。
希望下周可以及时的进行调整和自我反馈,越来越觉得有时候自我反馈和总结比一味的刷题更加重要。另外希望下周最后一周能对暑假所有所学的进行查漏补缺,把该补的题补完,该记录的模板概念都学完,并且做好对未来学习算法的规划。
二、本周学习新内容
1.求区间1-n之间与m互质的数的个数、
求不互质的个数
$互质=总数-不互质的个数$
ll primeNum(ll p, ll m) { |
2.求1-N含有0的个数
ll getCount0(ll num) |
3.博弈论
Nim博弈:
即n堆个物品,每次可以里面取任意个。
所有数异或如果为0则先手输
所有数异或如果不为0则先手胜
for (int k = 1; k <= n; k++) |
4.数论
1.唯一分解定理
任何一个大于1的自然数N,都可以被分解为有限个质数的乘积
即
其中$P_i$均为质数且$P_1<P_2<P_3<P_4$
这样的分解称为$N的标准分解式$
2.欧几里得算法
int gcd(int a,int b){ |
ax + by = d = gcd(a, b)
$通解 = 特解 + 齐次解$
齐次解 :
ax + by = gcd(a,b);
通解:
设d = gcd(a, b)
所以 x = r b / d, y = -r a / d;
x = x0 + b / d * k
y = y0 - a / d * k
在欧几里得算法中
b = 0 时:ax + by = gcd = a, 既:x = 1, y = 0;
b != 0 时:
ax + by = gcd = d (本层) |
bx1 + (a - [a / b] * b) * y1 = d |
所以:
x = y1 |
3.同余定理
- 证明:
其实就是余数在相减的过程中被减去了
两个数同余,那么他们的平方也同余,他们加上同样的数也同余
4.逆元
逆元 —— 广义化的倒数 - 知乎 (zhihu.com)[https://zhuanlan.zhihu.com/p/449221995]
定义x为mod p下的逆元
$a*x\equiv1 \pmod p$,且a与p互质
扩展欧几里得定理:
有解的条件是a与p互素
求解$a*x\equiv1 \pmod p$
转化为$ax+py=1$
即可以使用扩展欧几里得,解出x,再对x进行处理
|
费马小定理:
p比较小,且a,p互质,满足:
$a^{p-1} \equiv 1 (\bmod {p})$
$x \equiv a^{p-2} \pmod p$
即可以用快速幂求解
|
快速求阶乘逆元 $O(n)$:
因为有如下一个递推关系。
$inv[i+1]=\frac{1}{(i+1)}!$
$inv[i+1]∗(i+1)=\frac1i!=inv[i]$
所以我们可以求出$n!$的逆元,然后逆推,就可以求出$1…n!$所有的逆元了。
逆递推式为:
$inv[i]=inv[i+1]∗(i+1) \ (\bmod p)$
正递推式为:
$inv[i]=inv[i-1]∗(i)^{-1}\ \ \ \ (\bmod p)$
所以我们可以求出 $∀i,i!,\frac1i$的取值了
即:
$\displaystyle \frac{1}{i!} \times(i-1)!=\frac{1}{i} \pmod p$
线性推逆元:
求一连串数字mod一个p的逆元
初始化$1^{−1}≡1(\bmod p)$
设 $p=k∗i+r,(1<r<i<p)$
即$k=\frac pi,r=p\bmod i $
$k*i+r \equiv 0 \pmod p$
左右同乘$r^{-1},r^{-1}$
最终可以化简得到
$i^{-1}\equiv -\lfloor \frac{p}{i} \rfloor*(p \bmod i)^{-1} \pmod p$
inv[1] = 1; |
5.素数筛
时间复杂度$O(n*log(logn))$
大O表示法中所展现的$logn$ 仅表示为对数级的数量级,一般认为以 2 为底,但并不做严格限制
/* 埃式筛原理:如果当前数组是质数,把他的所有倍数都筛掉 */ |
注意i=2
若是n以内包括n,则要i<=n
每个合数都用最小的质数去筛
核心:保证prime[j]*i,即被筛的那个数,里面的最小质因子是prime[j],而不是i的因子。如果除通了,说明已经prime[j]已经是i的最小的质因子了。
|
解析
prime[j]%i!=0
,由于prime[j]
从小到大遍历的质数数组中的一个。除不通,说明i的最小质因子比prime[j]
还大,此时所有i的质因子都比primes[j]
大,则相乘的那个数的最小质因子一定是prime[j]
prime[j]%i==0
,则说明prime[j]
已经便利到了i的最小质因数,如果此时不break;
,则后面所筛掉的数,也就是prime[j]*i的最小质因子不是prime[j]
,而是i的因子。不能保证prime[j]
就是prime[j]*i
的最小质因子,那么后面就会被重复筛掉,从而不能达到每个数只被筛一次的算法
6.GCD和LCM
性质:
可重复贡献:
$gcd(a,b,c)=gcd(gcd(a,b),c)=gcd(a,gcd(b,c))$
$gcd(a,0)=a,gcd(a,b)=gcd(a,-b)$
$gcd(a,b)=gcd(b,a\bmod b)$
$gcd(a,b)=d, gcd(a/d,b/d)=1$
多个值的GCD和LCM
7.矩阵快速幂
第i行第j列的数=第i行的第k个数*第j列的第k个数
代码
for (int i = 0; i < N; i++) |
8.卡特兰数
求解方法
通项公式法:
$Cat_n=\frac{C^{n}_{2n}}{n+1}=\frac{(2n!)}{(n+1)!n!}=C_{2n}^{n}-C_{2n}^{n-1}$
递推法:
$Cat_n=\Sigma Cat_k*Cat_{n-k},Cat_0=1$
ll c[N]={1};
void catalan(int n)
{
for (int i = 1; i <= n; i++)
{
c[i] = 0;
for (int j = 0; j < i; j++)
{
c[i] += c[j] * c[i - 1 - j];
}
}
}$Cat_n=\frac{4n-2}{n+1}*C_{n-1},Cat_0=1$
优化
针对大数取模,可以使用通项公式
$Cat_n=\frac{C^{n}_{2n}}{n+1}$
配合预处理阶乘逆元求解
针对特大数,可用递推公式,配合高精度乘法/加法
三、题解一览
1.郑州大学 - K -魔法数
题目标签
数论
题目描述
定义满足以下条件的数为”魔法数”:
* 该数是正整数
* 该数的正约数有55个
16的正约数有1、2、4、8、16,因此16是魔法数。
20的正约数有1、2、4、5、10、20,因此20不是魔法数。
给定 l 和 r,求区间 [l,r]中魔法数的个数。
题目解析
结论题,知道结论就非常好做
一个大于1的正整数N,如果它的标准分解式为:
那么它的正因数个数为
所以求因子为五个的数
那么只有一种可能,他是某个质数的四倍,也就是$N=P_1^{4}$
所以先打素数筛,再把所取的数开四次方,求得范围
然后利用容斥原理,前缀和相减即可得到答案
通过代码
|
2.组合数学1 - B - Visible Trees
题目标签
容斥原理
题目描述
给出一个m*n的方格,每个格子里都种了树
和树在一条直线上的视线都会被挡住,问在起点能看见多少树
题目解析
如图所示,只有视线的第一颗树会被看到,后面的树都被遮挡住了,可见被挡住的树都和第一颗树都在同一条直线上。
而后面被挡住的树的坐标$(x_i,y_i)$,自然也就是第一棵树的坐标$(x_1,y_1)$的倍数。
即$x_i=kx_1,y_i=ky_1$
所以$gcd(x_i,y_i)!=1$,也就是被挡住的数都是可以约分的
反过来说,没有被挡住的树,就是坐标$(x,y)$互质的。
而我们需要求$n*m$对的方格里的被挡住的树,也就是我们只需要.
对第$1-n$列,求出第$i$列,与坐标$i$互质的$y$的个数就可以了
通过代码
|
3.组合数学1 - F - Ignatius and the Princess III
题目标签
组合数学;dp
题目描述
把一个数拆分成若干个数,问方案有几种
拆成的数可以一样,方案之间顺序不一样的只能算一种
比如5可以拆成
$(1,1,1,1,1) (1,1,1,2) (1,1,3) (1,4) (1,2,2) (2,3) (5)$
题目解析
如果用dfs,会超时
使用动态规划的思想,建立dp数组dp[i][j]
代表的是数字i被拆成最大数不超过j的方案数
比如dp[2][2]
就有$(1,1) (2)$两种方案
于是我们可以推出动态转移方程
当我们遍历时,
j>i,则可以都可以赋值为
dp[i][j-1]
,因为当前数字也不过i这么大,就算能限制的数字j大于i,也没用,还是上一次的赋值j=i,则可以赋值为
dp[i][j-1]+1
,因为j从$i-1→i$的过程中,新增加的一种方案,只有单独一个$(i)$j<i,则可以赋值为
dp[i][j-1]+dp[i-j][j]
,当j<i时,能出现的方案两种- 不选j——就是j-1时的所有方案
- 选j———-剩下的数i-j组成的所有方案
两种方案相加即可
于是可以得到动态转移方程
$\begin{align}&\\ \begin{cases}i>j,dp[i][j]=dp[i][j-1]+dp[i-j][j] \\ i=j,dp[i][j]=dp[i][j-1]+1\\i<j,dp[i][j]=dp[i][j-1]\end{cases} &\end{align}$
通过代码
|
4.排列组合 - G - 排列组合
题目标签
有n种物品,并且知道每种物品的数量。要求从中选出m件物品的排列数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有”AB”,”BA”两种
题目描述
母函数
题目解析
使用指数型母函数,生成解
即把组合问题的加法与幂
级数的乘幂对应起来
不断将当前的一个括号内的多项式,与下一个括号内的多项式相乘
即可得到最终的每一个多项式的系数
但是要将系数now[n]*fac[n]
注意:
- 最后打印结果要保留整数,使用
printf("%.0f\n",res);
- 每次乘完,要将nxt复制到now,并将nxt清零
通过代码
|
5.郑州大学 - A - 想要更多的0
题目标签
二分;数学
题目描述
给你一个数n,求一个 [0,n]之间最大的整数m,使得输出区间 [m,n]之间的所有数时至少输出了k个0,如果无解请输出-1
1<= n,k <= 10^18^
题目解析
本题两个问题有待解决:、
- 数据范围过大
数据范围过大,思路是二分查找答案
查询一个点的0的个数,和n的0的个数进行比较,如果比较出来是不够k个
那么high=mid-1,如果够了或者超了那么low=mid
问题是边界的处理
如何获取前n个数含有的0的个数
使用
ll getCount0(ll num)
{
if(num < 0) return 0;
if(num == 0) return 1;
ll base = 1;
ll sum = 1;
ll n = num;
while (n != 0) {
//区别在于这一行代码,减掉了1
sum += base * (n / 10 - 1);
int cur = n % 10;
if (cur == 0) {
sum += num % base + 1;
} else if (cur > 0) {
sum += base;
}
base *= 10;
n = n / 10;
}
return sum;
}
通过代码
|