[原创]数论个人模板
作为一个ACMer不可能没有自己的模板,并不是别人的模板你都能看的懂,所以有自己的模板很重要,下面是我自己一边培训一边整的数论模板,内容很少都是一些数论基础模板(毕竟不是大牛),但是此博客会一直更新的,有新内容我会马上添加进来的,如果有错误,请指出,谢谢。
1、数论基础
1)、数学公式
1^3+2^3+3^3+……+n^3=[n(n+1)/2]^2
斐波那契数列通项公式为:
f(n)=1/sqrt(5)(((1+sqrt(5))/2)^n+((1-sqrt(5))/2)^n)
若取前面k位的话:
ans= -0.5*(log10(5.0))+m*log10((sqrt(5.0)+1.0)/2));
ans -= (int)ans; ans=pow(10.0,ans);ans *= 10^(k-1);取整
对于a^b取前几位:
LL Pow2(LL a , LL b) {
double s = log10(double(a)) * b - LL(log10(double(a)) * b);
s = pow(10,s);
return s * 100;//这里是取a^b前三位
}
给一串不定长数字、空格字符,获取里面的数字。
C++
#include<sstream>
string s;
while (getchar() != '\n');
getline(cin,s);
int index = 0;
stringstream ss(s);
while (ss >> a[index]) ++index;
C
while (getchar() != '\n');
while ((buf = getchar()) != '\n')
if (buf >= '0' && buf <= '9') {
ungetc(buf,stdin);
scanf("%d",&data[count ++]);
}
2)、九余数定理
给一个数取每一位上的数字相加直到相加等于小于10的数,678=6+7+8=21=2+1=3;
ans = n%9 ; if(ans==0 )ans=9;
3) 、辗转相除法求最大公约数
int gcd(int x, int y){ return (!y)?x:gcd(y, x%y); }
4)、威尔逊定理
,其中p为素数。
5)、裴蜀定理(或贝祖定理)
若a、b为gcd(a,b) = d; 一定存在 ax+by = d;
6)、扩展欧几里得算法(求逆元)
http://blog.csdn.net/zhjchengfeng5/article/details/7786595
ax≡c(mod b). 等价于 ax+by=c,求最小x
通解方程:
x =x0 + (b/gcd)*t(t为>=0的整数)
y = y0 -(a/gcd)*t (t为>=0的整数)
LL e_gcd(LL a,LL b,LL &x,LL &y){
LL d=a;
if(b==0){
x=1;y=0;
}
else{
d=e_gcd(b,a%b,y,x);
y-=(a/b)*x;
}
return d;
}
LL cal(LL a,LL b,LL c){
LL x,y;
LL gcd=e_gcd(a,b,x,y);
if(c%gcd!=0) return -1;
LL t=b/gcd;
return (x* c / gcd % t + t) % t;
}
7)、中国剩余定理(同余式)
a .、数组元素互质的情况
x≡a1(mod m1)
x≡a2(mod m2)…..
int Chinese_Remainder(int a[],int m[],int len) // a[]存放余数 m[]存放两两互质的数
{
int i,d,x,y,Mi;
int ret=0;
int M=1;
for (i=0;i<len;i++) M*=m[i];//M是所有质数的乘积
for (i=0;i<len;i++)
{
Mi=M/m[i];//Mi是除m[i]外的所有质数的乘积
d= e_gcd (Mi,m[i],x,y);//Mi*x≡1(mod m[i])->x为Mi模m[i]的数论倒数
ret=(ret+x*Mi*a[i])%M;//X= ∑ a[i]*x*Mi
}
return (M+ret%M)%M;
}
b 、m数组元素不一定互质的情况
LL Chinese_Remainder (LL n,LL m[],LL a[]){
LL m1,r1,m2,r2,flag=0,i,d,x,y,c,t;
m1=m[0],r1=a[0];
flag=0;
for(i=1;i<n;i++) {
m2=m[i],r2=a[i];
if(flag)continue;
d = e_gcd (m1,m2,x,y);
c=r2-r1;
if(c%d) {//对于方程m1*x+m2*y=c,如果c不是d的倍数就无整数解
flag=1;
continue;
}
t=m2/d;//对于方程m1x+m2y=c=r2-r1,若(x0,y0)是一组整数解,那么(x0+k*m2/d,y0-k*m1/d)也是一组整数解(k为任意整数)
x=(c/d*x%t+t)%t;// x0=x*c/d,y0=x*c/d;保证x0是正数
r1=m1*x+r1;//新求的r1就是前i组的解,Mi=m1*x+M(i-1)=r2-m2*y(m1为前i个m的最小公倍数);对m2取余时,余数为r2;//对以前的m取余时,Mi%m=m1*x%m+M(i-1)%m=M(i-1)%m=r
m1=m1*m2/d;//m1为新的最小公倍数
}
if(flag)return -1;
if(n==1&&r1==0)return m1;//结果不能为0
return r1;
}
8)、筛法求素数、求因子个数
a 、运用筛法打印素数表
int prime[N]={0};
int flag[N]={0};
int Count[N]={0};
void PrimeTable(){
int index=0;
for(int i=2;i<=N;i++){
if(!flag[i]){
prime[index++]=i;
for(int j=i*i; j<=N; j+=i) {
flag[j]=1;
}
}
}
}
b 、求X的因子个数,通过刚刚的素数表来计算
void Count_factor(int x){
int i=0;
int xx=x;
Count[xx]=1;
while(x>=prime[i]){
int cc=0;
while(x%prime[i]==0){
x/=prime[i];
cc++;
}
i++;
Count[xx]=Count[xx]*(cc+1);
}
}
c、求X因子的和(不加本身打表)
void init(){
Count[0] = Count[1] = 0;
for(int i = 2;i < N;i++){
Count[i] = 1;
}
for(int i = 2;i <= N/2; i++){
for(int j = i+i;j < N;j += i) {
Count[j] += i;
}
}
}
9)、费马小定理(快速幂取模)
__int64 qpow(__int64 a,__int64 b,__int64 m){
__int64 ans=1;
while (b>0){
if (b&1) ans=(ans*a)%m;
b >>= 1;
a=(a*a)%m;
}
return ans;
}
10) 、矩阵快速幂
int n,m;//n矩阵大小,m矩阵个数
struct matrix{
int a[1][1];
} origin,res;
//计算矩阵相乘
matrix multiply(matrix x,matrix y){
matrix temp;
memset(temp.a,0,sizeof(temp.a));
for(int i = 0; i < n; i++){
for(int j = 0; j < n ; j++){
for(int k = 0; k < n; k++) {
temp.a[i][j] += x.a[i][k] * y.a[k][j];
}
}
}
return temp;
}
void quick_matrix(int m){
while(m){
if(m&1) res = multiply(res,origin);
m = m >> 1;
origin = multiply(origin,origin);
}
}
11)、欧拉函数
//1--n-1所有与n互质的数的和为:Sum = n*(eular(n)/2)
//暴力求n互质的个数
int eular(int n){
int ret=1,i;
for(i=2;i*i<=n;i++){
if(n%i==0){
n/=i;ret*=i-1;
while(n%i==0) {
n/=i;ret*=i;
}
}
}
if(n>1) ret*=n-1;
return ret;
}
//内存允许情况下
LL prim[N], p[N];
LL index;
void find_prim()
{
index = 0;
for(LL i = 2; i <= N; i++){
if(!p[i]){
prim[index++] = i;
for(LL j = i+i; j <= N; j+=i){
p[j] = 1;
}
}
}
}
LL enlur(LL a,LL index)
{
LL s = 1;
if(a == 0) return 0;
LL i = 0 ,tt = 0;
while(prim[i] < a && i < index){
tt = 0;
if(a%prim[i] == 0){
while(a%prim[i] == 0){
a/=prim[i];
tt++;
}
}
s *= tt+1;
i++;
}
if(a > 1) s *= 1+1;
return s;
}
//优化一般首选,打印欧拉函数表
__int64 phi[N]={0};
void phi_table(){
phi[1]=1;
for(int i=2; i<=N; i++){
if(!phi[i]){
for(int j=i; j<=N; j+=i){
if(!phi[j]) phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
}
12)、反素数
//求n以内因子最多的最小的那个X
LL bestnum , maxsum , n;//bestnum最小的,maxsum因子个数
LL prime[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
void dfs(LL index,LL temp,LL sum){
if(temp > n) return;
//sum记录当前的因子个数,maxsum小就更新
if(sum > maxsum){
maxsum = sum;
bestnum = temp;
}
//当因子个数相同时,取值最小的
if(sum == maxsum && bestnum > temp) bestnum = temp;
for(LL i=1;i<=63;i++){
if(n < temp*prime[index]) break;
temp *= prime[index];//累乘到当前数
dfs(index+1,temp,sum*(i+1));
}
}
调用// dfs(0,1,1);
//给一个数,求一个最小的正整数,使得它的因子个数为。
LL maxsum,n;
void dfs(int index,LL temp,int sum){
if(sum > n) return;
if(sum == n && maxsum > temp) maxsum = temp;
for(int i=1;i<=63;i++){
if(maxsum / prime[index] < temp || sum*(i+1) > n) break;
temp *= prime[index];
if(n % (sum*(i+1)) == 0) dfs(index+1,temp,sum*(i+1));
}
}
2、组合数学
1)、错排
选新娘问题,有N(N<=20)对新人,其中有M个新郎找错新娘。问有多少种可能?
代码:
__int64 a[25];
void init(){
//这个是错排打表
a[0] = 1; a[1]=0; a[2]=1;
for(i=3;i<=20;i++){
a[i]=(i-1)*(a[i-1]+a[i-2]);
}
}
__int64 Cmn(__int64 m, __int64 n){
if( m==n || n == 0) return 1;
LL s = 1 , t = 1;
for(__int64 i = m; i > m - n; i--) s *= i;
for(__int64 i = 1; i <= n; i++) t *= i;
return s/t;
}
int main(){
int tcase,m,n,i,j;
__int64 sum;
init();
scanf("%d",&tcase);
while(tcase--){
scanf("%d%d",&m,&n);
sum=Cmn(m,n)*a[n];//Cmn是组合
printf("%I64d\n",sum);
}
return 0;
}
2)、全排列
直接用c++stl库里面的东西!输出的结果直接就是字典序
sort(str,str+len);//这步是必须要的,先排序,len数组元素的长度
cout<<str<<" 所有全排列的结果为:"<<endl;
do{
for(int i=0;i<len;i++) cout<<str[i]<<” “;
cout<<endl;
}while(next_permutation(str,str+len));
3)Cmn%p问题
//小数据情况下a[N][m]即可
void init(){
int i,j;
for(i=1;i<=N;i++){
a[i][1]=i%p;
a[i][0]=1;
}
for(i=2;i<=N;i++){
for(j=1;j<=N;j++){
a[i][j]=(a[i-1][j]+a[i-1][j-1])%p;//这个是公式
}
}
}
3、博弈论
1)、威佐夫博奕(Wythoff Game)
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
int m,n;//两堆石子个数
while(cin>>m>>n)
{
int ans = floor(abs(m - n)*(1.0+sqrt(5.0))/2.0);
if (ans == min(m,n)) cout<<0<<endl;
else cout<<1<<endl;//胜
}
2)、尼姆博奕(Nimm Game)
有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
int m,p[101];
while(cin>>m,m)
{
int ans=0;
for(int i = 0; i < m; i++)
{
cin >> p[i];
ans ^= p[i];
}
if(ans) cout << "Win" << endl;
else cout << "Not win" << endl;
//如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0
int s = 0;
for(int i = 0; i < m; i++){
if(p[i] > (p[i] ^ ans) )s++;
}
cout<<s<<endl;
}