一.數論部分
1.素數測試
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <map>
using namespace std;
#define Times 10
typedef long long LL;
map<LL,int>mp;
LL random(LL n)
{
return LL ((double)rand()/RAND_MAX*n+0.5);
}
//避免出現了相乘取餘前造成的溢出
LL multi(LL a,LL b,LL mod)
{
LL ans=0;
while(b)
{
if(b&1)
{
b--;
ans=(ans+a)%mod;
}
else
{
b/=2;
a=(a+a)%mod;
}
}
return ans;
}
LL Pow(LL a,LL b,LL mod)
{
LL ans=1;
while(b)
{
if(b&1)
{
b--;
ans=multi(ans,a,mod);
}
else
{
b/=2;
a=multi(a,a,mod);
}
}
return ans;
}
// 二次探測找到一個不是x=1或者x=p-1的解就一定不是素數
bool witness(LL a,LL n)
{
LL d=n-1;
while(!(d&1))//冪是偶數時有探測的必要
d>>=1;
LL t=Pow(a,d,n);
while(d!=n-1 && t!=1 && t!=n-1)//如果存在一個非1或非p-1的解,就不是素數
{
t=multi(t,t,n);
d<<=1;
}
return t==n-1 || d&1 ;
}
bool miller_rabin(LL n)
{
if(n==2)
return true;
if(n<2 || !(n&1))
return false;
for(int i=1;i<=Times;i++)
{
LL a=random(n-2)+1;
if(!witness(a,n))
return false;
}
return true;
}
LL gcd(LL a,LL b)
{
if(b==0)
return a;
return gcd(b,a%b);
}
LL pollard_rho(LL n,int c)
{
LL x,y,d,i=1,k=2;
x=random(n-2)+1;
y=x;
while(1)
{
i++;
x=(multi(x,x,n)+c)%n;
d=gcd(y-x,n);
if(1<d && d<n)
return d;
if(y==x)
return n;
if(i==k)
{
y=x;
k<<=1;
}
}
}
void find(LL n,LL c)
{
if(n==1)
return ;
if(miller_rabin(n))
{
mp[n]++;
return ;
}
LL p=n;
while(p>=n)
p=pollard_rho(p,c--);
find(p,c);
find(n/p,c);
}
2.素數篩法等
分段篩素數:
100W 差不多接近8W素數
void make_prime()
{
m.clear();
unsigned __int64 tmp;
memset(cnt,true,sizeof(cnt));
for(int i=1;i<=n_prime;i++)
{
tmp=l/num[i];
while(tmp*num[i]<l||tmp<=1)
tmp++;
for(unsigned __int64 j=tmp*num[i];j<=r;j+=num[i])
{
if(j>=l&&j<=r)
cnt[j-l]=0;
}
}
if(l==1)
cnt[0]=0;
for(unsigned __int64 i=0;i+l<=r;i++)
if(cnt[i]&&i+l<=r&&i>=0)
m[i+l]++;
}
求因子和
void factor_sum()
{
for(int i=1;i<=maxn;i++)
prime[i]=1;
for(int i=2;2*i<=maxn;i++)
for(int j=2*i;j<=maxn;j+=i)
prime[j]+=i;
prime[1]=0;
}
另外一種求法:
(pn^(xn+1)-1)/(pn-1)的累乘
3.歐拉函數
phi[i]=i*(1-1/p1)*(1-1/p2)*(1-1/p3)...
單獨求歐拉值:
long long euler(long long x)
{
if(x==1)
return 0;
long long i,res=x;
for(i=2;i*i<=x;i++)
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0)
x/=i;
}
if(x>1)
res=res/x*(x-1);
return res;
}
遞推求歐拉值:
void Phi()
{
for(int i=1;i<=maxn;i++)
phi[i]=i;
for(int i=2;i<=maxn;i+=2)
phi[i]/=2;
for(int i=3;i<=maxn;i+=2)
if(phi[i]==i)
for(int j=i;j<=maxn;j+=i)
phi[j]=phi[j]/i*(i-1);
}
歐拉公式的引伸:
小於或等於n的數中,與n互質的數的總和爲:φ(x) * x / 2。(n>1)
線性求歐拉值:
const int maxn=1000001;
long long euler[maxn];
long long prime[maxn/3];
bool isprime[maxn];
void solve()
{
int cnt=0;
memset(isprime,1,sizeof(isprime));
for(int i=2;i<maxn;i++)
{
if(isprime[i])
{
prime[++cnt]=i;
euler[i]=i-1;
}
for(int j=1;j<=cnt&&prime[j]*i<maxn;j++)
{
isprime[prime[j]*i]=0;
if(i%prime[j]!=0)
euler[i*prime[j]]=euler[i]*euler[prime[j]];
else
{
euler[i*prime[j]]=euler[i]*prime[j];
break;
}
}
}
euler[1]=0;
}
4.Lucas
對於組合數C(n,m)方便求出來的情況,直接求fac,然後用逆元解
void init()
{
fac[0]=1;
for(int i=1;i<=p;i++)
fac[i]=fac[i-1]*i%p;
}
long long C(long long n,long long m)
{
if(n<m)
return 0;
long long ans=fac[n]*Pow(fac[n-m]*fac[m]%p,p-2,p)%p;
return ans;
}
不然的話,就只好一步一步逆元的求解組合數
long long C(long long n,long long m)
{
if(n<m)
return 0;
long long ans=1;
for(int i=1;i<=m;i++)
ans=(((n-m+i)%p)*Pow(i,p-2,p)%p)*ans%p;
return ans;
}
long long Lucas(long long n,long long m)
{
if(m==0)
return 1;
return Lucas(n/p,m/p)*C(n%p,m%p)%p;
}
二.矩陣
一類是具體題目具體 構造的,類型比較多,也有涉及到數的實部虛部,狀態什麼的,沒什麼好寫的。
這裏翻牆可以看到一篇比較好的資料和習題:http://zobayer.blogspot.com/2010/11/matrix-exponentiation.html
另一種就是對平面內點的變換了,具體看下圖:
轉自 matrix67
具體實現如下:(注意全部操作應爲,操作矩陣 * 目標矩陣)
#define PI acos(-1.0)
struct Matrix
{
double m[4][4];
}E,matrix[10010];
void init()
{
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
{
if(i==j)
E.m[i][j]=1.0;
else
E.m[i][j]=0.0;
}
}
Matrix init_translation(double _x,double _y)
{
Matrix ans=E;
ans.m[1][3]=_x;
ans.m[2][3]=_y;
return ans;
}
Matrix init_zoom(double l)
{
Matrix ans=E;
ans.m[1][1]=l;
ans.m[2][2]=l;
return ans;
}
Matrix init_up_down()
{
Matrix ans=E;
ans.m[2][2]=-1;
return ans;
}
Matrix init_left_right()
{
Matrix ans=E;
ans.m[1][1]=-1;
return ans;
}
Matrix Multi(Matrix A,Matrix B)
{
Matrix ans;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
{
ans.m[i][j]=0;
for(int k=1;k<=3;k++)
ans.m[i][j]+=A.m[i][k]*B.m[k][j];
}
return ans;
}
Matrix init_rotate(double r)
{
Matrix ans=E;
ans.m[1][1]=cos(r);
ans.m[1][2]=-sin(r);
ans.m[2][1]=sin(r);
ans.m[2][2]=cos(r);
return ans;
}
Matrix last(Matrix A,Matrix B)
{
Matrix ans;
for(int i=1;i<=3;i++)
for(int j=1;j<=1;j++)
{
ans.m[i][j]=0;
for(int k=1;k<=3;k++)
ans.m[i][j]+=A.m[i][k]*B.m[k][j];
}
return ans;
}
三.方程之類
擴展歐幾里得:
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
else
{
long long ans=exgcd(b,a%b,x,y);
long long t=x;
x=y;
y=t-a/b*y;
return ans;
}
}
//開始的時候對於方程 ax+by=c 可以方程兩邊同時除以gcd(a,b)
// c%gcd(a,b)!=0 時無解
// 得到的解x0,y0實際上爲特解,通解是 x=x0+k*b/gcd(a,b) y=y0-k*a/gcd(a,b)
//若是求x的最小值,首先要還原(因爲原方程解的是ax+by=1的解)x*=c,然後是x%=b...
//求方程的r=gcd(a,b)個解:For(i=1...r) ans[i]=x0+(i-1)*b//這裏的b就是前面的b/gcd(a,b)
擴展歐幾里得求逆元:
求 (a/b)%mod
=((a%mod)*(b^-1%mod))%mod
由exgcd可以得到
方程 b*b^-1 = 1(%mod)
即 b*b^-1+k*mod = 1
so
b^-1 = (x%mod+mod)%mod;
特殊情況:
當b是質數時,由費馬小定理可知a^(p-1)=1(mod p)
so
a^-1 = a^(p-2)
一元線性同餘方程組:
// 已知 n 和 x=a[i](mod b[i])
//大體思路是兩兩運算,推出新的方程,然後運算
//已知輸入的線性方程爲
// x=a1(mod b1)
// x=a2(mod b2)
// ...
// x=an(mod bn)
//先看第一個 x-a1=b1y1 x-a2=b2y2
// 聯立:b1y1-b2y2=a2-a1
bool solve()
{
a[0]=a[1],b[0]=b[1];
for(int i=2;i<=n;i++)
{
int a0=b[0];
int b0=b[i];
int c0=a[i]-a[0];
int x0,y0;
int r=gcd(a0,b0);
if(c0%r!=0)
return false;
else
{
a0/=r;
b0/=r;
c0/=r;
exgcd(a0,b0,x0,y0);
x0*=c0;
x0=(x0%b0+b0)%b0;
a[0]=b[0]*x0+a[0];
b[0]=b[0]*b0;
}
}
return true; // a[0]爲小於[b1,b2...bn]的非負整數解
}
中國剩餘定理:
int CRT(int r)
{
long long M=1;
long long i,d,x0,y0,ans=0;
for(i=1;i<=r;i++)
M*=w[i];//好像很容易溢出的樣子,CRT弱爆了
for(i=1;i<=r;i++)
{
d=M/w[i];
exgcd(d,w[i],x0,y0);
ans=(ans+d*x0*a[i])%M;
}
while(ans<= 0)
ans+=M;
return ans;
}
//用w[]表示模數(兩兩互質),a[]表示餘數,返回值是以M爲模的解
高次同餘方程:
// 已知 A^x=B(mod C)
#include <iostream>
#include <cstdio>
#include <cmath>
#define maxn 65535
using namespace std;
struct hash
{
int a,b,next;
}Hash[maxn*2];
int flg[maxn+66];
int top,idx;
void ins(int a,int b)
{
int k=b&maxn;
if(flg[k]!=idx)
{
flg[k]=idx;
Hash[k].next=-1;
Hash[k].a=a;
Hash[k].b=b;
return ;
}
while(Hash[k].next!=-1)
{
if(Hash[k].b==b)
return ;
k=Hash[k].next;
}
Hash[k].next=++top;
Hash[top].next=-1;
Hash[top].a=a;
Hash[top].b=b;
}
int find(int b)
{
int k=b&maxn;
if(flg[k]!=idx)
return -1;
while(k!=-1)
{
if(Hash[k].b==b)
return Hash[k].a;
k=Hash[k].next;
}
return -1;
}
int gcd(int a,int b)
{
return !b?a:gcd(b,a%b);
}
int exgcd(int a,int b,int &x,int &y)
{
int t,ans;
if(!b)
{
x=1;
y=0;
return a;
}
ans=exgcd(b,a%b,x,y);
t=x;
x=y;
y=t-a/b*y;
return ans;
}
int Inval(int a,int b,int n)
{
int x,y,e;
exgcd(a,n,x,y);
e=(long long)x*b%n;
return e<0?e+n:e;
}
int pow_mod(long long a,int b,int c)
{
long long ans=1;
a%=c;
while(b)
{
if(b&1)
{
ans=ans*a%c;
}
a=a*a%c;
b>>=1;
}
return ans;
}
int BabyStep(int A,int B,int C)
{
top=maxn;
++idx;
long long buf=1%C,D=buf,K;
int i,d=0,tmp;
for(i=0;i<=100;buf=buf*A%C,i++)
if(buf==B)
return i;
while((tmp=gcd(A,C))!=1)
{
if(B%tmp) return -1;
++d;
C/=tmp;
B/=tmp;
D=D*A/tmp%C;
}
int M=(int)ceil(sqrt((double)C));
for(buf=1%C,i=0;i<=M;buf=buf*A%C,++i)
ins(i,buf);
for(i=0,K=pow_mod((long long)A,M,C);i<=M;D=D*K%C,++i)
{
tmp=Inval((int)D,B,C);
int w=find(tmp);
if(tmp>=0&&w!=-1)
return i*M+w+d;
}
return -1;
}
int main()
{
int A,B,C;
while(scanf("%d%d%d",&A,&C,&B)!=EOF,A||B||C)
{
B%=C;
int tmp=BabyStep(A,B,C);
if(tmp<0)
printf("No Solution\n");
else
printf("%d\n",tmp);
}
return 0;
}
佩爾方程:
已知方程 x^2 - d*y^2 = 1
可知迭代方程:
x(n) = x(n-1)*x1 + d*y(n-1)*y1
y(n) = x(n-1)*y1 + y(n-1)*x1
用矩陣快速冪求解:
| x(n) | = | x1 dy1 |^(n-1) | x1 |
| y(n) | = | y1 x1 | | y1 |
高斯模板 // czyuan大神
int equ,var;/// equ個方程,var個變量
int a[maxn][maxn];
int x[maxn];///解
bool free_x[maxn];///不確定變元
int free_num;
void debug()
{
for(int i=0;i<equ;i++)
{
for(int j=0;j<var+1;j++)
printf("%d ",a[i][j]);
printf("\n");
}
printf("\n");
}
int gcd(int a,int b)
{
if(b==0)
return a;
return gcd(b,a%b);
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
/// -2表示浮點解 -1表示無解 0表示唯一解 大於0表示自由變元個數
int gauss()
{
int i,j,k;
int max_r;
int col=0;
int ta,tb;
int LCM;
int tmp;
int free_x_num;
int free_index;
for(k=0;k<equ&&col<var;k++,col++)
{
max_r=k;
for(i=k+1;i<equ;i++)
{
if(abs(a[i][col])>abs(a[max_r][col]))
max_r=i;
}
if(max_r!=k)
{
for(j=k;j<var+1;j++)
swap(a[k][j],a[max_r][j]);
}
if(a[k][col]==0)
{
k--;
continue;
}
for(i=k+1;i<equ;i++)
{
if(a[i][col]!=0)
{
LCM=lcm(abs(a[i][col]),abs(a[k][col]));
ta=LCM/abs(a[i][col]);
tb=LCM/abs(a[k][col]);
if(a[i][col]*a[k][col]<0)
tb=-tb;
for(j=col;j<var+1;j++)
a[i][j]=a[i][j]*ta-a[k][j]*tb;
}
}
}
for(i=k;i<equ;i++)
if(a[i][col]!=0)
return -1; /// 無解
if(k<var) /// 無窮解
{
for(i=k-1;i>=0;i--)
{
free_x_num=0;
for(j=0;j<var;j++)
{
if(a[i][j]!=0&&free_x[j])
{
free_x_num++;
free_index=j;
}
}
if(free_x_num>1)
continue;
tmp=a[i][var];
for(j=0;j<var;j++)
{
if(a[i][j]!=0&&j!=free_index)
tmp-=a[i][j]*x[j];
}
x[free_index]=tmp/a[i][free_index];
free_x[free_index]=0;
}
return var-k;
}
for(i=var-1;i>=0;i--)
{
tmp=a[i][var];
for(j=i+1;j<var;j++)
if(a[i][j]!=0)
tmp-=a[i][j]*x[j];
if(tmp%a[i][i]!=0)
return -2;
x[i]=tmp/a[i][i];
}
return 0;
}
java版的高斯消元(分數) hdu2449.java
四.不是很重要的東西
[1].n!的素因子分解中素數p的冪爲[n/p]+[n/p^2]+[n/p^3]+...
[2].C(2n,n)中素數p的冪爲([2n/p]-2[n/p])+([2n/p^2]+2[n/p^2])+...+([2n/p^t]-2[n/p^t])
t=[logp(2n)]
[3].梅森素數:
Lucas-Lehmer判定法:(注意期間平方運算可能會溢出)
對於第p個梅森數Mp=2^p-1,R1=4,對於k>=2,根據Rk=(R(k-1))^2-2(mod Mp)得到Rk序列,則Mp是素數,當且僅當R(p-1)=0(mod Mp)
bool Lucas-Lehmer(int p)
{
long long ans=1;// 開始寫的是 long long ans=(1<<p)
ans<<=p;//然後試出來,那個1是默認的int,必須改成 ans=((long long)1<<p);
ans--;
printf("%lld",ans);
long long R[65]={0,4};
for(int i=2;i<=p-1;i++)
{
long long tmp=multi(R[i-1],R[i-1],ans);
R[i]=(tmp-2)%ans;// multi寫一個a*b%m就好
}
if(p==2) //特判
return true;
else
{
if(R[p-1]==0)
return true;
return false;
}
}
[4]畢達哥拉斯三元組
x=m*m-n*n; y=2*m*n; z=m*m+n*n (m,n不同爲奇數和偶數)
[5]佩爾方程
X^2 - dY^2 =1 令最小解爲X1,Y1
Xn=Xn-1*X1+d*Yn-1*Y1
Yn=Xn-1*Y1+Yn-1*X1
[6]迴文數個數
求10^n範圍內迴文數的個數:
if(n%2==0)
f(n)=2*(10^(n/2)-1);
else
f(n)=11*10^((n-1)/2)-2;
[7]大數數字前n項
可以對10取對數,得到的值減去floor部分,再將剩下的部分還原
[8]勒讓德兩平方數之和定理:R(N)=4*D1(N)-4*D3(N)
D1(N)=(整除N 且滿足d=1(mod 4)的正約數d 的個數),
D3(N)=(整除N 且滿足d=3(mod4)的正約數d 的個數)。
[9]指數循環節
a^b mod c = a^(b%phi(c))%c (b>phi(c))
[10]Catalan數 C(2n,n)/(n+1)
高精度樣例可以參照:hdu1131
[11] sigma(gcd(x,y))
sigma(gcd(i,n))=sigma(j*euler(n/j)+n/j*euler(j)) (n%j==0)
[12] 五邊形數定理 參照:http://zh.wikipedia.org/wiki/%E4%BA%94%E9%82%8A%E5%BD%A2%E6%95%B8%E5%AE%9A%E7%90%86
微積分啥的,遇到了,拿微積分表當模版吧
傅里葉變換,不會,這模版估計也不會用,跳過
莫比烏斯反演和polya計數等還得學,或者直接丟掉
其他遇到了,再學吧,再補充。