【复习】幼儿园数论复习
害怕
扩欧
因为ax1+by1==gcd(a,b) bx2+(a%b)y2==gcd(a,b) 所以ax1+by1==bx2+(a%b)y2所以ax1+by1==bx2+(a-(a/b)b)y2所以ax1+by1==ay2+b(x2-(a/b)y2),所以x1==y2,y1==(x2-(a/b)*y2)//exgcd int exgcd(int a,int b,int &x,int &y) { if(!b) { x=1; y=0; return a; } int xx,yy; int tmp=exgcd(b,a%b,xx,yy); x = yy; y = (xx-(a/b)*yy); return tmp; }
中国剩余定理(不扩展)
对于一个同余方程组例如 X==a1(mod m1) X==a2(mod m2) X==a3(mod m3) ..... X==an(mod mn) 就是点兵那个玩意,并且有m1,m2,m3,....mn互质的话,就可得出最小的正整数解X。 我们设定P为m1m2m3...mn,那么Mi为M/mi,那么就有 X==(M1(M1在模m1意义下的逆元)a1 + M2(M2在m2意义下的逆元)a2 + M3(M3在模m3意义下的逆元)*a3.....)%P。扩展中国剩余定理
说实话,这玩意和孙子定理的区别还是有些大的,,唯一的相同点就是这两个用来解决对问题是一样的(感觉扩中比普中简单?orzorz),而这东东适用范围更广,可以适用于m1,m2,m3....mn不互质的情况。 例如我们针对两个线性方程组 X==a1 ( mod b1) X==a2 ( mod b2) 我们将两个式子展开 X=b1x1 + a1,X=b2x2 + a2,那么有b1x1 + a1=b2x2 + a2,那么我们移项b1x1+b2x2=a2-a1(因为我们知道x2是一个未知数,由于除了扩欧求解的时候用到之后不会用到,那么我们不变号),该请出扩欧啦,我们求出最小的正整数解x1,将这个x1代入b1*x1+a1=K(我们设得到的数为K),那么我们就合并得到了一个新的方程组X==K(mod lcm(b1,b2))(lcm为最小公倍数),我们一直合并下去,最后得到的那个K就是答案啦!欧拉线性筛
代码说话#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #include<algorithm> using namespace std; const int maxn = 10000005; int cnt,pri[maxn];//有那些质数 bool ispri[maxn];//i是否是质数 int mindiv[maxn];//i的最小质因子 int phi[maxn];//i的欧拉函数 int mindivq[maxn];//i的最小质因子个数 int d[maxn];//i的约数个数 int sumd[maxn];//i的约数和 int sp[maxn];//i的最小素因子的等比数列和(1+p+p^2+p^3)(为了方便计算约数和用) int miu[maxn];//i的莫比乌斯函数 void eula(int n) { ispri[1]=ispri[0]=0; miu[1]=1; memset(ispri,1,sizeof ispri); for(int i=2;i<=n;i++) { if(ispri[i]) { pri[++cnt]=mindiv[i]=i;//这个数是质数且其最小质因子为本身 } phi[i]=i-1;//质数i的欧拉函数为i-1 miu[i]=-1;//有一个本质不同的质因数 sumd[i]=i+1;//质数约数和显然本身和1 sp[i]=i+1; } for(int j=1;j<=cnt&&pri[j]*i<=n;j++) { int k = i*pri[j]; ispri[k]=0; mindiv[k]=pri[j];//每个数只会被最小的质因数筛掉 if(i%pri[j]==0) { phi[k]=phi[i]*pri[j];//对于一个数是另一个数的倍数其欧拉函数有倍数关系 mindivq[k]=mindivq[i]+1;//与i有相同的最小质因数且为i倍数 d[k]=d[i]/(mindivq[i]+1)*(mindivq[k]+1);//我们在i的基础上又乘了一个最小的质因数,因为d[i]=求积(pri_i个数+1)(pri_i为分解的质因数),这么一来这就很显然啦 miu[k]=0;//有一个质因子的个数起码为2,miu为0 sp[k]=sp[i]*pri[j]+1//类比秦九韶 sum[k]=sum[i]/sp[i]*sp[k];//去除最小质因子的等比数列之后乘上当前的等比数列 break; } phi[k]=phi[i]*phi[pri[j]];//对于两个互质的数的欧拉函数有积性性质,pri[j]显然与i互质 mindivq[k]=1;//可怜只有一个 d[k]=d[i]*d[pri[j]];//约数个数是显然的积性函数 miu[k]=-miu[i];//多了一个本质不同的质因数 sumd[k]=sumd[i]*sumd[pri[j]];//多了一个本质不同质因数就多*(1+p) sp[k]=1+pri[j];//显然只有1和pri[j] } } } int main() { eula(1000000); for(int i=1;i<=20;i++) cout<<pri[i]<<" "; }我们发现欧拉筛可以筛约数和。其原理是某个数的约数和为(1+p1+p1^2+p1^3.....)(1+p2+p2^2+p2^3+p2^4+....)(1+p3+p3^2+p3^3+p3^4+....)...*(1+pn+pn^2+....pn^k)这个p和k是在唯一分解完后的质因数和质因数上的指数。
miller_rabin
由于质数,那么对于x^(n-1)%n一定等于1,那么我们将n-1分解成2^r*d,然后之后一个个判断就可以了。(其实其中还有很多细节,详见code)#include<algorithm> #include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll xy[20] = {0,2,3,5,7,11,13,17,19,61,0}; /* ll ksc(ll a,ll b,ll mod) { ll ans = 0; for(a%=mod;b;b>>=1,a=(a+a)%mod) { if(b&1) ans = (ans+a)%mod; } return ans; } */ ll ksc(ll a,ll b,ll n) { ll tmp = a*b - ((ll)((long double)a/n*b+0.5) )*n; return tmp<0?tmp+n:tmp; } ll ksm(ll a,ll b,ll mod) { ll ans = 1; for(;b;b>>=1,a=ksc(a,a,mod)) if(b&1) ans = ksc(ans,a,mod); return ans; } bool miller_rabin(ll n) { ll x,y,r=0,d; if(n==2) return 1; if(n<2||n%2==0) return 0; d = n-1; while(d%2==0) d/=2,r++; for(ll i=1;i<=9;i++) { if(xy[i]==n) return 1; ll x = ksm(xy[i],d,n); for(ll j=1;j<=r;j++) { y = ksc(x,x,n); if(y==1&&x!=1&&x!=n-1) return false; x = y; } if(x!=1) return false; } return true; } int main() { ll x; while(~scanf("%lld",&x)) { if(miller_rabin(x)) puts("Y"); else puts("N"); } }快速乘(注意精度问题!)
ll ksc(ll a,ll b,ll n) { ll tmp = a*b - ((ll)((long double)a/n*b+0.5) )*n; return tmp<0?tmp+n:tmp; }