7.6 数学 学习笔记
7.6 gyx math
第一部分 康托展开
变进制数
给定无穷数集\(S=\{a_0,a_1,a_2,...,a_n,...\},a_0=1\)
定义变进制数:第\(i\)单位是上一位单位的\(a_i\)倍
要求变进制数\((A)_s=\overline{x_nx_{n-1}...x_1x_0},0\leq x_i <a_{i+1}\)
\(k\)进制数:\(\forall i>0,a_i=k\)
结合我们常见的\(k\)进制数来理解,变进制数其实就是每一位上的进位制不一样
对于变进制数\(A\)有
广义康托展开
变进制数下的进制转换
对于两个变进制数\(S_1,S_2\),给出\((A)_{S_1}\),求\((A)_{S_2}\)
一种可操作的思路是\((A)_{S_1}\rightarrow (A)_{10}\rightarrow (A)_{S_2}\)
其中第一步操作可以直接利用变进制数的定义\(O(n)\)求出
第二步操作采用短除法,每次除以当前位的进制,余数即为该位结果
int s1[N],s2[N],a[N],res[N];
void Cantor(int len){
ll A=0;
//(A)s1→(A)10
for(int i=len-1;i>=0;i--) A=(A+a[i])*s1[i];
//(A)10→(A)s2
for(int i=0;A;i++) res[i]=A%s2[i+1],A/=s2[i+1];
}
康托展开
康托展开:将任意十进制自然数\((A)_10\)变换为阶乘进制数\((A)_!\)
阶乘进制数:要求\(a_i=i\)
若\((A)_!=\overline{x_nx_{n-1}...x_1x_0}\),有
要求\(\forall i\in [0,n],0\leq x_i< i+1\),此时康托展开唯一
逆康托展开
将任意阶乘进制数\((A)_!\)变换为十进制自然数\((A)_{10}\)
与广义康托展开一致,这里的\(\prod_{i=0}^na_i=i!\)
参考上面广义康托展开代码
排列与变进制数
引例:火星人
任何一个\(1,...,n\)的排列\(p_1,p_2,...,p_n\)都对应唯一的阶乘进制数
构造双射变换\(f:P→S_!:x_i=rank\_suf(p_i)\)
即\(p_i\)在后缀\({p_i,p_{i+1},...,p_n}\)中的排名,从0开始
对于排列变阶乘进制数,采用的数据结构要求有维护数集,插入删除,查询排名的功能
使用树状数组和线段树都是 \(O(nlogn)\) 的
对于阶乘进制数变排列,采用的数据结构要求有维护数集,查询排名为\(k\)的数并删除的功能
- 线段树/树状数组上二分 复杂度\(O(nlogn)\)
- 线段树/树状数组套二分 复杂度\(O(nlog^2n)\)
推荐写线段树上二分
int t[4*N];
void pushup(int x){
t[x]=t[x<<1]+t[x<<1|1];
}
void add(int x,int l,int r,int p,int dlt){
if(l==r){t[x]+=dlt;return;}
int mid=(l+r)>>1;
if(p<=mid) add(x<<1,l,mid,p,dlt);
else add(x<<1|1,mid+1,r,p,dlt);
pushup(x);
}
int sum(int x,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return t[x];
int res=0,mid=(l+r)>>1;
if(ql<=mid) res+=sum(x<<1,l,mid,ql,qr);
if(qr>mid) res+=sum(x<<1|1,mid+1,r,ql,qr);
return res;
}
int query(int x,int l,int r,int cnt){
if(l==r) return l;
int mid=(l+r)>>1;
if(cnt<=t[x<<1]) return query(x<<1,l,mid,cnt);
return query(x<<1|1,mid+1,r,cnt-t[x<<1]);
}
ll n,a[N],x[N];
int main(){
n=read();
//排列变阶乘进制数
for(int i=n-1;i>=0;i--) a[i]=read();
for(int i=0;i<n;i++){
x[i]=sum(1,1,n,1,a[i]);
add(1,1,n,a[i],1);
}
for(int i=n-1;i>=0;i--) printf("%d ",x[i]);
printf("\n");
//阶乘进制数变排列
for(int i=n-1;i>=0;i--) x[i]=read();
memset(t,0,sizeof t);
for(int i=1;i<=n;i++) add(1,1,n,i,1);
for(int i=n-1;i>=0;i--){
a[i]=query(1,1,n,x[i]+1);
add(1,1,n,a[i],-1);
}
for(int i=n-1;i>=0;i--) printf("%d ",a[i]);
return 0;
}
例题
板板
给定\(\;1,...,n(n\leq 10^5)\;\)的排列\(\;p_1,p_2,...,p_n\;\)和一个正整数\(\;\alpha\;\):
操作\(\;1\;\):将当前排列排名增加\(\;k\;(k\leq 10_{18})\)
操作\(\;2\;\):输出当前排列对应的\(\sum_{i=1}^np_i\times \alpha ^i \;mod\;998244353\)
操作次数不大于排列长度
Tips:$20!\approx2\times 10^{18},25! \approx 1.5\times 10^{25} $
Bonus:操作\(\;1\;\)改为继承历史版本
我们观察\(k\)的范围可以知道,增加一次排名最多影响的是最后20位的数字,所以完成整道题影响到的数字范围最多是后25位,所以前面的数字的答案我们可以提前处理好
剩下25位转成阶乘进制数暴力修改就可以了
事板子题
逆序对
跟上面题一样,只是操作2改成了求当前排列的逆序数,用树状数组\(O(logn)\)完成查询
给定多重集\(S=\{a_1,...,a_n\}\)的排列\(p_1,p_2,...,p_n\)
求该多重集有多少个本质不同的排列,字典序小于当前排列
答案对某给定常数\(m\)取模,不保证质数
第二部分 数论初步
带余除法和整除
对于整数\(a,b\),存在唯一的两个整数\(q,r\)使得:
还有一种形式(恒等变换常用):
还有一种形式(数论分块常用):
当\(r=0\)时我们称\(a\)整除\(b\),记作\(a|b\)
此时也称\(b\)为\(a\)的倍数,\(a\)为\(b\)的约数
整除的性质
算数基本定理
任何一个自然数\(N\),可以唯一分解成有限个质数的乘积
其中\(p_1<p_2<p_3<...<p_n\)均为质数,指数\(k_i\)均为正整数
这样的分解称为标准分解式
通过标准分解式我们可以求出\(\sigma_0\)以及\(\sigma_1\),它们分别代表约数个数和约数和
下面给出公式
以及等比数列求和公式
素数无限定理
正整数集中包含无限个素数
证明:构造+反证法
复杂度常识
- 素数分布
其中\(\pi(x)\)表示不大于x的素数个数
- 调和级数
- 素数调和级数
集合含义
\(A=p_1^{a_1}*p_2^{a_2}*...*p_n^{a_n},\;B=p_1^{b_1}*p_2^{b_2}*...*p_n^{b_n}\)
裴蜀定理
\(\forall a,b,d,(a,b)|d\;\)等价于\(\;\exists u,v\in N,ua+vb=d\)
用来判定二元一次方程无解
麦肯基定理:二元一次方程\(y=ax+b\)无非负数解最大整数为\(a*b-a-b\)
同余
若\(a\equiv b(mod\;p)\),则存在整数\(k\)使得\(a=b+kp\)
\(a\equiv b(mod\;p)\Leftrightarrow p|b-a\)
\(a\equiv b(mod\; p),a\equiv b(mod\;p)\Leftrightarrow a\equiv b(mod\;[p,q])\)
\((k,p)=d,ka\equiv kb(mod\; p)\Leftrightarrow a\equiv b(mod\;\frac pd)\)
例题:迁徙
剩余系
对特定的正整数\(p\),某个整数集中的所有数模\(p\)所得的余数域
如果一个剩余系中包含了这个正整数所有可能的余数,就称之为是模\(p\)的完全剩余系
整数的所有运算限制在剩余系中各种性质仍成立
简化剩余系
所有的\(n\)满足\(0<n\leq p,(n,p)=1\)构成了模\(p\)的简化剩余系
记这样\(n\)的个数为\(\phi(p)\)
若\((a,p)=1\),当\(x\)取遍膜\(p\)的简化剩余系时,\(ax\)也取遍膜\(p\)的简化剩余系
欧拉函数求逆元
最一般的情况:\(a^{\phi(p)-1}\equiv a^{-1}(mod\; p)\)
拉格朗日插值
定理:有n+1个点值可以求出一个n次多项式
基函数
拉格朗日插值
两式联立得
即可以用来推出中国剩余定理的一般解
离散对数
求解\(y^x=z(mod p)\)
双向搜索优化
即 BSGS算法
板子:[SDOI2011]计算器
线性筛
完全积性函数
积性函数
不只是筛质数,凡是积性函数都可以用线性筛处理,比如说:
欧拉函数
莫比乌斯函数
约数函数组
具体的板子看这里
注:第二部分有删减,其中素数与合数、约束与倍数、欧几里得算法、扩展欧几里得算法、同余方程、快速幂与快速乘、逆元、欧拉定理、线性求逆元、扩展欧拉定理、中国剩余定理部分删去,可以从本人以前的blog中获得
第三部分 构造
- 在组合对象的变化过程中,寻找不变量
- 使用不等式导出上下界 / 排除可能方案
- 根据同余关系(尤其是奇偶性)判定某种类型组合对象不存在
- 多做题,自己体会
基于既定性质的构造
小题1
给定n个数,他们的和为M
你的任务是去掉一个数,然后把剩下的集合分成两组
使得每一组内的和都\(\leq \lfloor\frac M2\rfloor\)
要求时间复杂度\(O(N)\),空间复杂度O(1)
从前往后扫,去掉刚好前缀和大于等于\(\lfloor\frac M2\rfloor\)的元素,左右两边即为答案
小题2
给定一个长度为n的字符串,字符集\(S=\{A,K,N,O,I\}\)
保证任意相邻的三个字符必定不同
问是否存在长度至少为\(\lfloor\frac n3\rfloor\)的回文子序列,有的话输出任意一个
从两边开始分别往右和往左每3个分成一组,根据题意组里不可能存在相同元素,那么左边的组和对应的右边的组,至少有一个重复的字母,我们每次选两边对应的组中相同的元素,构造出来的就是长度至少为\(\lfloor\frac n3\rfloor\)的回文子序列。
小题3
给定一张 3N 个点的图
保证其中有一个大小为 2N 的团
找到一个大小为 N 的团
每次选两个不连通的点,根据题意这两个点最多有一个在团里,那么我们每次删掉这两个不连通的点,删去了N对之后,最多有N个在团中的点被删掉,最少有N个不在团里的点被删掉,剩下点的必定都在团里
小题4
给定一张\(N(N\leq 10^5)\)个点的无向图,在图上放置监视哨,已知:
约定每个监视哨的势力范围为距离不超过 1 的所有点
保证图中存在一个放置 K个监视哨的方案,使得所有点都被覆盖
现在监视哨升级到势力范围为距离不超过 2 的所有点,请提供一个放置至多 K 个监视哨的覆盖方案
我们把研究对象放到一条边上,考虑开始的时候一条边的端点放置监视哨的情况,
若两个点都没有放监视哨,情况不存在,
若一个点放置了监视哨,另一个没有放,那么在改变后还在那个位置放监视哨和之前情况等效,
若两个都放了监视哨,那么两点覆盖的范围是对称的,
所以我们的方案就是每次随机选一个没有被覆盖的点,然后更新答案
增量法
小题1
给定\(n\),用\(1...n\)每个数恰好一次,使得算式的结果为24
可以使用四则运算和小括号
当\(n<4\)时,无解,当\(n=4\)和\(5\)的时候有特殊解,对于后面的情况,采用增量法,如果是奇数,对于\(n=5\)的情况在后面乘上\((i-(i-1))\),如果是偶数,在\(n=4\)的情况在后面乘上\((i(i-1))\)
小题2
汉诺塔,构造最少的移动次数,判断当前一步具体的移动
我们知道次数的递推式是\(f(n)=2*f(n-1)+1\),定义函数F(x,A,B,C),这里的x指当前A柱顶端放的块编号是多少,A指起始柱,B指辅助转移的柱,C指目标柱,递归式就是
小题3
对于所有\(1...n\)的排列,构造一个顺序使得任意相邻的两个排列均可通过交换相邻的一对位置得到
仍然采用递归,对于一组长度为\(n-1\)已经排好顺序的排列,每个排列复制为\(n\)个,取每顺序为奇数的排列从左往右在每个空隙插入\(n\)这个数,取每顺序为偶数的排列从右往左在每个空隙插入\(n\)这个数,得到的新的排列组就是长度为\(n\)时的答案,此外有一个很有意思的发现,就是新加入的数在排列组中是呈S型走向的
小题4
对于所有长度为\(n\)的\(2^n\)的\(01\)串,构造一个循环的顺序使得任意相邻的两个串只有一位不同
可以把题目转换成超立方体的遍历问题,因为超立方体上每一个相邻的点坐标都只有一位不同,对于一个\(n\)维立方体,我们可以把它转化成\(n-1\)维来处理,首先,一个\(n\)维立方体,是由两个\(n-1\)维立方体拼成的,那么
当起点和终点在同一个\(n-1\)维立方体上,我们直接遍历两个立方体,在关键立方体上拆除一条边连上另外一个立方体,
当起点和终点不在同一个\(n-1\)维立方体上,在一个立方体中走到\(x\)点转移到另一个立方体上再走到终点,\(x\)点任选
小题5
给定\(n\)个互不相同的单词,第\(i\)个单词将要在文章中出现\(a _ i\)次
现在将单词重编码为二进制串,需保证文章不出现歧义
最小化文章总长度
Bonus: 重编码为\(k\)进制
构造二叉哈夫曼树,求树的带权路径长度
具体看这里
基于图论的构造
小题1
给定一个序列\(\{d_i\}\),判断是否能构造一个简单无向图,给出方案
Havel定理告诉我们,可以先按大小对序列排序,然后让d最大的点与次大的d个点分别连边,不断操作,直到建出完整的图,或是出现负数
欧拉回路
需熟练掌握欧拉回路(通路)的判定、构造和性质
欧拉回路:一个环经过所有边
判定
回路 | 通路 | |
---|---|---|
无向图 | 图中所有点均为偶点 | 图中只有2两个奇点 |
有向图 | 底图连通,各点出入度相同 | 底图连通,只存在两个点出入度有差异,且差为1 |
构造
通过判定可构造
性质
深刻的组合性质:每个点出入平衡,每条边恰好被访问一次
哈密顿回路
没有比较好的判定定理,但是
对于一个竞赛图,一定有哈密顿通路
对于一个强连通竞赛图,一定有哈密顿回路
竞赛图缩点后肯定是一条链
掌握特殊情况下的求法即可
哈密顿回路:一个环经过所有点
竞赛图:有向完全图(底图是完全图的有向图)
n阶竞赛图:底图是n阶完全图的有向图
求强联通竞赛图的哈密顿回路
第一步,先找到一条哈密顿通路,具体做法类似链表
- 找到一条点指向链头,把它插入到链头
- 找到一条点被链尾指向,把它插到链尾
- 找到一条点被链中的一个点指向并指向原链中的下一个点,把它插到两点之间
第二步,由通路变回路
从链尾开始扫,扫到第一个指向链头的点,得到一条不完整环
然后搞一堆骚操作就整好环了
//这个部分最好画图理解,图是一个环长一条尾巴的样子。
r=0;//这里是紧接着上面找完通路。先把r置为0表示还没有找到初始的环。
for(int i=l;i;i=nxt[i])//r是环上最靠近链的点,r->l是环上的边,r->nxt[r]是链上的边。
if(r){//尝试在环中插入点i
for(int j=l,k=r;;k=j,j=nxt[j]){
if(a[i][j]){//在环上找到一个可以作为nxt[i]的点。
nxt[k]=nxt[r];//j作为了i的后继,那么本来j的前驱k就要另找一个后继了。(这里注意nxt[r]不一定是i,因为可能前面的一些点没有插入成功)
if(k!=r)nxt[r]=l;//本来没有连上的环上的边要连上(k=r的话r的后继在上一句话已经改了,不是l了)
l=j,r=i;break;//根据l和r的定义修改l和r
}
if(j==r)break;//确实有可能当前无法插入,但是后面的点一定会有插入成功的,那时这个点也就会进入环内。
}
}
else if(a[i][l])r=i;//这里找到了初始的环
nxt[r]=l;//这里把最后一条边连上。
求竞赛图的哈密顿回路
缩点后是个环,每个强连通分量都有一个环,都接起来就行了
void work() {
int l = r = 1;
for (int i = 2;i <= n; i++) {
if (ed[i][l]) nxt[i] = l, l = i;
else if (ed[r][i]) nxt[r] = i, r = i;
else for (int j = l; ; j = nxt[j])
if (a[i][nxt[j]]) { nxt[i] = nxt[j], nxt[j] = i; break; }
}
}
稠密图的哈密顿回路
不会