[专题总结]数学综合

前言

    这里记录一些较琐碎的数学知识。

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 eq 0)),却常常忽略了((c eq 0)),这个条件。类似的例子还有等比数列求和公式,当然那个例子更引人注目的是((a eq 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} ight }),则(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
",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]数论

这是神仙题。巧妙地运用了式子

[left lfloor frac{n+m}{k} ight floor-left lfloor frac{n}{k} ight floor-left lfloor frac{m}{k} ight floor ]

的值只能等于(0)(1)的性质以及等式

[sum_{i=1}^{n}i=sum_{i=1}^{n}sum_{d|i}phi(d)=sum_{i=1}^{n}phi(i)left lfloor frac{n}{i} ight floor ]

原根

定义、性质、定理都挺多的。我也就记了几个结论,不知道这东西真要考的话会怎么考。
知识点推荐OI Wiki-原根
Upd:知道怎么考了,就是在模质数p意义下可以把1~p-1唯一对应成p的原根的幂,这样相乘就变成了指数相加,实现乘法转加法。

二项式反演

二项式反演及其应用

线性基

实际上就是维护一个上三角矩阵。
线性基学习笔记

原文地址:https://www.cnblogs.com/gosick/p/12189543.html