这一周复习了数论内容:
组合数:
常用的有lucas定理以及求逆元的操作,思维题比较多,
常常运用于统计方案个数。
有时候直接式子难以直接求,需要化简。
常用的转化方式:
通过添减项(+1=+C(n,n)),利用C(n,m)=C(n-1,m-1)+C(n-1,m)来合并各项。
另外还有一些问题可以通过转化模型最后变为组合数,这种情况可以先想想什么是容易求的,再想想可否转化。
例如求长度为n的单调不降序列个数问题的常用转化
求组合数的常用方法:
关于法四,具体是:
筛出1-n中所有质数,筛出每个数最小的质因子p[i]。
求C(n,m)
开一个cnt[n]的数组。
for(i=n~1) 对于每个i(质数跳过),拆成两个数p[i]和i/p[i],cnt[p[i]]和cnt[i/p[i]]都加上cnt[i],cnt[i]赋为0。
这样之后cnt[p]存的就是质因子p的指数。
因为n以内的质数有 n/logn 个,for每个指数,快速幂是log的,因此最终求C(n,m)的复杂度是O(n)的。
容斥原理
还是有些不熟悉。
常用的DFS
void dfs(int pos,LL num,int dep)
{
if(pos==(n+1)||num>m) return;
LL nnum=1LL*a[pos]*1LL*num/gcd(a[pos],num);
int select=(dep%2==0)?-1:1;
ans+=select*m/nnum;
dfs(pos+1,num,dep);
dfs(pos+1,nnum,dep+1);
}
dfs(1,1,1);
然后学到了奇妙的:
二进制枚举子集的方法
非常巧妙也非常优。
两个套路:
与起来为k/交集为k :包含k的 - 包含k+1的 + 包含k+2的…
或起来为k/并集为k :k包含的 - k-1包含的 + k-2包含的…
线性筛
除了筛素数外常用于求积性函数.
套路:
直接套式子求出
f(1),f(p),f(pk)
f
(
1
)
,
f
(
p
)
,
f
(
p
k
)
(因数比较少很好讨论)
对于每个数 x 筛出:
other[x]这个数除去其最小质因子p剩下的数(
xpk
x
p
k
)。
ind[x]这个数最小质因子的指数。
每次对于一般的数
x=i∗p
x
=
i
∗
p
,就直接利用积性函数的性质乘就行了:
f(x)=f(i)∗f(p)
f
(
x
)
=
f
(
i
)
∗
f
(
p
)
对于 i%p==0,
f(x)=f(pk)∗f(other(x))
f
(
x
)
=
f
(
p
k
)
∗
f
(
o
t
h
e
r
(
x
)
)
(这个p^k就直接x/other(x))
以之前facsum的代码为例:
void shai()
{
f[1]=1; other[1]=1; ind[1]=1;
memset(is,0,sizeof(is));
is[1]=1;
for(int i=2;i<=n;i++)
{
if(!is[i])
{
pri[++ptot]=i;
f[i]=2-i;
other[i]=1;
ind[i]=1;
}
for(int j=1;j<=ptot;j++)
{
int p=pri[j];
LL x=1LL*p*i;
if(x>n) break;
is[x]=1;
if(i%p==0)
{
ind[x]=ind[i]+1;
other[x]=other[i];
if(other[i]==1) //p^k
f[x]=((mod-1LL*p*ind[x])%mod+(ind[x]+1)%mod)%mod;
else
f[x]=(1LL*f[other[x]]*f[x/other[x]])%mod;
break;
}
ind[x]=1;
other[x]=i;
f[x]=(1LL*f[i]*f[p])%mod;
}
}
}
逆元
O(n)递推求逆元
CRT
非常有用的工具,常用于模数不是质数,最后拆分分别求答案再合并。
欧拉定理
aφ(m)≡1(mod m)
a
φ
(
m
)
≡
1
(
m
o
d
m
)
gcd(a,m)=1
g
c
d
(
a
,
m
)
=
1
推论:
ax≡ax mod φ(m)+φ(m)(mod m)
a
x
≡
a
x
m
o
d
φ
(
m
)
+
φ
(
m
)
(
m
o
d
m
)
不要求
gcd(a,m)=1
g
c
d
(
a
,
m
)
=
1
常用于简化指数。
关于这周的测验:
组合数一类仍然比较弱,但一般的基本知识是知道的。
下一周仍然分出一天半左右集中复习数论。