[专题总结]数学综合
前言
这里记录一些较琐碎的数学知识。
BSGS
博文:yyb的BSGS算法
设要求解的方程是\(a^{x}\equiv c(mod\;p)\),设\(p=tm-b\),注意:
- 一开始就要判断一下c是否为1,即解是否为0,然后枚举t的范围是\((0,m+1]\),t一定不能枚举0,因为这样子\(p=tm-b\)可能会为负数。
- 一个看上去不足道的细节:当\(a=0\)时不能使用BSGS算法求解。但这个细节的意义却值得注意。我们推式子时习惯于\(a=b \Leftrightarrow ca=cb (c\neq 0)\),却常常忽略了\((c\neq 0)\),这个条件。类似的例子还有等比数列求和公式,当然那个例子更引人注目的是\((a\neq 1)\)。
[SDOI2013]随机数生成器WA了N发的我痛定思痛
exBSGS
注意除d的时候要判断x是否可以取0。
群论
其实OI中用到的只是置换群以及那两个定理吧?
知识点是在《组合数学》上学的,因为可以勾勾画画,所以就不搬上来了\(qwq\)
幸好是在《组合数学》上学的,不然看各种说法不一的题解很容易迷茫的。
那就说几道题。
[POI2009]SLO
置换+贪心,这种模型好像是第二次见了,值得注意。
[HNOI2008]Cards
友好的burnside定理入门题。注意要加入单位元才能构成置换群,简单DP与burnside结合计数似乎是常见套路。
一个关于n阶循环群的结论
设n阶循环群\(G=\left \{p_{0},p_{1},...,p_{n-1} \right \}\),则\(p_{i}\)的循环个数为\(gcd(i,n)\)。
[SHOI2006]有色图
刚开始颓题解时觉得好难。其实核心思想就是把计算点置换\(f\)的不动点数\(c(f)\)转化为计算边置换\(g\)的不动点数\(c(g)\),其它的都好推。
[Hnoi2010]物品调度
挺好的题。就是总感觉不相信这样做是对的。
Miller_rabin&Pollard_rho
[P4718]【模板】Pollard-Rho算法
这是真duliu题。
做完这题我才知道我写的Pollard_rho有多假。
天真的我直接上慢速乘,T飞,必须用__int128或long double才能过,但是我的代码用long double还过不了。。。
代码和注释可以看一下。
#include<bits/stdc++.h>
#define rg register
using namespace std;
typedef long long LL;
const int mod=1e9+7;
LL read(){
LL x=0,w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return w?-x:x;
}
int X,A,B;
inline void init(){
srand(time(0));
X=rand(),A=rand()+1,B=rand()+1;
}
inline int rd(){return X=(1ll*X*A+B)%mod;} //自己写rand()可以取到范围更大的随机值,而且会快一点(然而我写的这个好像很不随机)
inline LL mul(LL a,LL b,LL p){return (__int128)a*b%p;}
//inline LL mul(LL a,LL b,LL p){return (a*b-(LL)((long double)a*b/p+1e-9)*p)%p;} 这个过不了
inline LL kpow(LL a,LL k,LL p){
LL res=1;
for(;k;k>>=1,a=mul(a,a,p))if(k&1)res=mul(res,a,p);
return res;
}
inline LL gcd(LL a,LL b){ //比较sao的gcd写法
while(b^=a^=b^=a%=b);
return a;
}
bool test(LL p,LL a){
LL d=p-1;rg int s=0;
for(;!(d&1);d>>=1,++s) ;
a=kpow(a,d,p);
while(s--){
LL b=mul(a,a,p);
if(b==1&&(a!=1&&a!=p-1)) return false;
a=b;
}
return a==1;
}
bool miller_rabin(LL p){ //没错,miller_rabin的终极境界就是:疯狂特判,能判就判,循环展开,面向数据(实测素数只取2和61就能过)
if(p==46856248255981ll||p<2) return false;
if(p==2||p==3||p==7||p==61||p==24251) return true;
return test(p,2)&&test(p,61);
}
LL pho(LL x){
LL s=rd(),t=s,c=rd()+1;
LL val=1,d;
for(rg int i=1;;i<<=1,s=t,val=1){ //题解里看到的优化,就是累乘127次再求gcd,这个闸值可以自己调
for(rg int j=1;j<=i;++j){
t=(mul(t,t,x)+c)%x;
val=mul(val,abs(t-s),x);
if(j%127==0&&(d=gcd(val,x))>1) return d;
}
if((d=gcd(val,x))>1) return d;
}
}
LL mx;
void divide(LL x){
if(x<=mx||x<2) return ; //x<2一定要写啊,不然T飞
if(miller_rabin(x)){mx=max(mx,x);return;}
LL d=x;
while(d==x) d=pho(x); //不知道为什么pollard_rho似乎经常返回x本身
while(x%d==0) x/=d;
divide(x),divide(d);
}
int main(){
init();
int cas=read();
while(cas--){
mx=0;LL x=read();
if(x==1) puts("1");
else{
divide(x);
if(mx==x) puts("Prime");
else printf("%lld\n",mx);
}
}
return 0;
}
博弈论
k-Nim游戏
有\(n\)堆石子\(a_{1},...a_{n}\),一次操作可以从\(1 \sim k\)堆石子中取任意数量的石子,不能取者败。
拿来主义:
结论是,设\(c_{i}=\sum_{j}[(a_{j}>>i)\&1]\),则先手必败的充要条件是\(\forall c_{i}\%(k+1)=0\),否则先手必胜。
[SDOI2011]黑白棋
转化为确定从左往右每对黑白棋之间的距离,共\(k/2\)对,相当于\(k/2\)堆石子。考虑求不合法方案数,设\(f[i][j]\)表示使得\(j\)个石子的\(c_{0} \sim c_{i-1}\%(k+1)\)都为0的方案数,转移枚举\(c_{i}\)的大小(必须能整除\(k+1\)),然后分给\(k/2\)堆石子。
设最高位为14。则\(f[15][i]\)的贡献为\(C(n-i-k/2,k/2)\),这里的证明挺有趣的,但懒得写了。
SG游戏&Nim游戏
博弈论好难啊啊啊。
学习资料
https://blog.csdn.net/A_Comme_Amour/article/details/79347291
集训队论文《组合游戏略述——浅谈SG游戏的若干拓展及变形》
[SP2021]PEBBMOV-Moving Pebbles
我认为很难想的一道题。先找出必败态以及后手模仿先手的思路都挺不错的。
数论函数&莫反
[BZOJ4173]数论
这是神仙题。巧妙地运用了式子
的值只能等于\(0\)或\(1\)的性质以及等式
原根
定义、性质、定理都挺多的。我也就记了几个结论,不知道这东西真要考的话会怎么考。
知识点推荐OI Wiki-原根。
Upd:知道怎么考了,就是在模质数p意义下可以把1~p-1唯一对应成p的原根的幂,这样相乘就变成了指数相加,实现乘法转加法。
二项式反演
线性基
实际上就是维护一个上三角矩阵。
线性基学习笔记