/*
定义: 对于正整数n,φ(n)是小于或等于n的正整数中,与n互质的数的数目;
例如: φ(8) = 4, 因为1,3,5,7均和8互质。
性质: 1. 若p是质数,φ(p)= p-1.
2. 若n是质数p的k次幂,φ(n)= (p-1)p^(k-1)
因为除了p的倍数都与n互质
3. 欧拉函数是积性函数,若m,n互质,φ(mn)= φ(m)φ(n)
根据这3条性质我们就可以退出一个整数的欧拉函数的公式,因为一个数总可以一些质数的乘积的形式。
E(k) = (p1-1)(p2-1)…(pi-1)*(p1^(a1-1))(p2^(a2-1))…(pi^(ai-1))
= k*(p1-1)(p2-1)…(pi-1)/(p1*p2*…pi)
= k*(1-1/p1)*(1-1/p2)…(1-1/pk)
在程序中利用欧拉函数如下性质,可以快速求出欧拉函数的值(a为N的质因素)
若(N%a==0 && (N/a)%a==0) 则有:E(N)=E(N/a)*a;
若(N%a==0 && (N/a)%a!=0) 则有:E(N)=E(N/a)*(a-1);
以下是2种求欧拉函数的算法
1 void init()
2 {
3 __int64 i,j;
4 e[1] = 1;
5 for(i=2;i<=N;i++)
6 if(!e[i])
7 {
8 for(j=i; j<=N; j+=i)
9 {
10 if (!e[j])
11 e[j] = j;
12 e[j] = e[j] / i * (i-1);
13 }
14 }
15 }
利用素数筛选:
void init()
{
__int64 i, j;
p[0] = 1; //记录素数个数
p[1] = 2;
for (i=3; i<N; i+=2)
{
if (hash[i])
continue;
p[++p[0]] = i;
for (j=i*i; j<N; j+=i)
hash[j] = true;
} //筛素数
e[1] = 1;
for (i=1; i<=p[0]; i++)
e[p[i]] = p[i] - 1; //初始化素数的phi
for (i=2; i<N; i++)
{
if(!e[i])
{
for (j=1; j<=p[0]; j++)
if (i % p[j]==0)
{
if (i / p[j] % p[j])
e[i] = e[i / p[j]] * e[p[j]];
else
e[i] = e[i / p[j] ]* p[j];
break;
} // 利用上述性质求解
}
}
return ;
}
明显第一种的编程复杂度要低很多
所以,一般情况下(N不是很大),采用第一种即可;
*/
/*
该题刚开始用最原始的方法,先筛出质数,
再利用X=( p1-1 )*( p2-1 )*....*( pn-1 )*( p1^( q1-1 ) )*( p2^( q2-1 ) )*...*( pn^( qn-1 ) ),
我就测试了一下3--3000000发现要好长的时间我就知道这种方法一定会超时;后来看了解题报告,
才知道有如下的线性筛选法( 我个人刚开始不太喜欢线性法,因为我与其比较过其他的筛选法,
发现并没有那么快,并且还要开那么大的数组,
后来发现线性筛选法可以与好多的题目的结合处理特别好,现在也开始钟情它了 );
在程序中利用欧拉函数如下性质,可以快速求出欧拉函数的值(a为N的质因素)
(1) 若(N%a==0 && (N/a)%a==0) 则有:E(N)=E(N/a)*a;
(2) 若(N%a==0 && (N/a)%a!=0) 则有:E(N)=E(N/a)*(a-1);
*/
#include<stdio.h>
#include<stdlib.h>
int num[3000024],prime[220000];
bool isprime[3000024]={0};
void eular( )
{
int count=0;
__int64 k;
for( int i=2; i<=3000000; i++ )
{
if( !isprime[i] )
{
prime[++count]=i;
num[i]=i-1;
}
for( int j=1; j<=count&&( (k=prime[j]*i)<=3000000 );j++ )
{
isprime[k]=1;
if( i%prime[j]==0 )
{
num[k]=num[i]*prime[j];
}
else num[k]=num[i]*( prime[j]-1 );
}
}
}
int main( )
{
int n,m;
eular();
while( scanf( "%d%d",&n,&m )!=EOF )
{
__int64 ans=0;
for( int i=n; i<=m; i++ )
ans+=num[i];
printf( "%I64d\n",ans );
}
return 0;
}
GCD Again
/*
欧拉函数
题目大意:给定一个N,求出所有满足条件GCD(M,N) > 1的M(0<M<N)的个数
算法分析
求出小于N且与N互质的数的个数,结果为:N-1-euler(N)。
单独求欧拉函数的模版:
int euler(int x)
{
int i,res = x;
for(i = 2;i < (int)sqrt(x*1.0) + 1;i++)
if(x % i == 0)
{
res = res / i * (i-1);
while(x % i == 0) //保证i一定是素数
x /= i;
}
if(x > 1)
res = res/x *(x-1);
return res;
}
*/
#include<iostream>
#include<cmath>
using namespace std;
#define maxn 100000000
int euler(int x)
{
int i,res = x;
for(i = 2;i < (int)sqrt(x*1.0) + 1;i++)
if(x % i == 0)
{
res = res / i * (i-1);
while(x % i == 0) //保证i一定是素数
x /= i;
}
if(x > 1)
res = res/x *(x-1);
return res;
}
int main()
{
int n;
while(scanf("%d",&n) && n)
printf("%d\n",n-1-euler(n));
return 0;
}
A Simple Math Problem
//矩阵快速幂
/*
If x < 10 f(x) = x.
If x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10);
|f(10) | |a0 a1 a2 ...a8 a9| |f(9)|
| f(9) | | 1 0 0 ... 0 0| |f(8)|
| .....| = |.. ... ... ... ..| | .. |
| f(2) | | 0 0 0 ... 0 0| |f(1)|
| f(1) | | 0 0 0 ... 1 0| |f(0)|
另A举证为10*10的举证,如上图。
可以推出:
(f(n),f(n-1),...,f(n-9))^(-1) = A^(n-9)*(f(9),f(8),...,f(0))^(-1)
*/
#include<iostream>
using namespace std;
__int64 k,m;
struct mat
{
int mar[10][10];
}a,b,tmp;
mat matrixmul(mat a,mat b)
{
int i,j,k;
for(i = 0;i < 10;i++)
for(j = 0;j < 10;j++)
{
tmp.mar[i][j] = 0;
for(k = 0;k < 10;k++)
tmp.mar[i][j] += (a.mar[i][k] * b.mar[k][j]) % m;
tmp.mar[i][j] %= m;
}
return tmp;
}
void matrix_binary()
{
while(k)
{
if(k & 1)
b = matrixmul(b,a);
a = matrixmul(a,a);
k = k >> 1;
}
}
int main()
{
int i;
while (scanf("%I64d%I64d",&k,&m) != EOF)
{
memset(a.mar,0,sizeof(a.mar));
for(i = 1;i < 10;i++)
a.mar[i][i-1] = 1;
memset(b.mar,0,sizeof(b.mar));
for(i = 0;i < 10;i++)
b.mar[i][i] = 1;
for(i = 0;i < 10;i++)
scanf("%d",&a.mar[0][i]);
if(k < 10)
{
printf("%d\n", k % m);
continue;
}
k -= 9;
matrix_binary();
int res = 0;
for(i = 0;i < 10;i++)
res += (b.mar[0][i] * (9-i)) % m;
printf("%d\n",res%m);
}
return 0;
}
Hello Kiki
/*
hdu 3579 Hello Kiki 中国剩余定理(不互质的情况)
对互质的情况,处理起来比较方便,可以直接套模板
本题给出不互质的模线性方程组,求出满足方程的最小正整数解
方案:对于不互质的模线性方程组,可以进行方程组合并,求出合并后的方程的解,这样就可以很快地推出方程的最终解。
两个方程合并的一种方法:
x = c1 (mod b1)
x = c2(mod b2)
此时b1,b2不必互质的。
显然可以得到x = k1 * b1 + c1 x = k2* b2 + c2,
两个方程合并一下就可以得到:k1 * b1 = c2 - c1 (mod b2),
这样可以设g=gcd(b1,b2),于是就有b1/g*k1-b2/g*k2=(c2-c1)/g,
显然判断(c2-c1)/g是否为整数就能判断是否存在解,
这样在经过类似的变换就能得到k1 = K (mod (b2/g)),
最后得到x = K*b1 + c1 (mod (b1 * b2/g))。
对于题目所给正整数的要求,只有一种反例,就是结果输出为0的情况,
这个可以特殊考虑,只需要考虑所有数的最小公倍数即可。
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
__int64 x, y, t;
__int64 egcd(__int64 a, __int64 b)
{
if (b==0)
{
x=1;
y=0;
return a;
}
else
{
__int64 e=egcd(b,a % b);
t=x;
x=y;
y=t-a/b*y;
return e;
}
}
__int64 gcd(__int64 x, __int64 y)
{
if (!x || !y)
return x > y ? x : y;
for (__int64 t; t = x % y; x = y, y = t);
return y;
}
__int64 mm[10],rr[10];
int main()
{
int T,Case,N;
__int64 m1,m2,r1,r2,d,c,t;
bool flag;
scanf ("%d",&T);
for(Case = 1; Case <= T; Case++)
{
scanf ("%d",&N);
flag=0;
for (__int64 i=0;i<N;i++)
{
scanf ("%I64d",&mm[i]);
}
for (__int64 i=0;i<N;i++)
{
scanf ("%I64d",&rr[i]);
}
m1=mm[0];
r1=rr[0];
for (__int64 i = 0; i < N - 1; i++)
{
m2=mm[i+1];
r2=rr[i+1];
if (flag)
continue;
d = egcd(m1, m2);
c = r2 - r1;
if (c % d)
{
flag = 1;
continue;
}
t=m2/d;
x=(c/d*x%t+t)%t;
r1=m1*x+r1;
m1=m1*m2/d;
}
if (flag)
printf ("Case %d: -1\n",Case);
else
{
if (r1==0&&N>1)
{
r1=mm[0];
__int64 ans=1;
for (int i=1;i<N;i++)
r1=gcd(mm[i],r1);
for (int i=0;i<N;i++)
ans*=mm[i];
r1=ans/r1;
}
if (r1==0&&N==1)
r1=mm[0];
printf ("Case %d: %I64d\n",Case,r1);
}
}
return 0;
}
Description has only two Sentences
/*
(x^k-1)*(y/(x-1))=0(mod a0),再将(y/(x-1))和a0先消掉公因数,就是要求最小k满足x^k=1(mod m)
k^euler(m)=1(modm),找euler(m)的因数中能满足k^x=1(mod m)的最小x(快速幂)
循环节:a^b=a^(b+t) (mod c) -> a^t=1(mod c) -> 满足a^x=1(mod c) 则 x=t*k;(抽屉原理)
euler: a^euler=1(mod c)
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int d[1000000];
typedef long long ll;
ll euler(ll n)
{
ll i,ans = n,t = n;
for( i = 2 ; i * i <= n ;i++ )
if( t%i == 0) {
ans -= ans/i;
while( t%i == 0) t /= i;
}
if( t > 1) ans -= ans/t;
return ans;
}
ll gcd(ll a,ll b)
{
return b?gcd(b, a%b):a;
}
ll fast_pow(ll a,ll b,ll c) //a^b%c
{
ll tmp=a%c,res=1;
while (b) {
if(b&1) res =res*tmp%c;
tmp=tmp*tmp%c;
b>>=1;
}
return res;
}
int main()
{
ll x,y,a;
while (cin>>x>>y>>a) {
ll k=y/(x-1);
if(k==0) {cout<<"1"<<endl;continue;}//这儿也可以不特判,写的时候是因为快速幂写错WA了以后乱加的<img alt="难过" src="http://static.blog.csdn.net/xheditor/xheditor_emot/default/sad.gif">
ll tmp=gcd(a,k);
if(gcd(a/tmp, x)!=1){ cout<<"Impossible!"<<endl;continue;};
ll res=euler(a/tmp),cnt=0;
for(int i=1;i*i<=res;i++)
{
if(res%i==0)
{
d[cnt++]=i;
if(res/i!=i)d[cnt++]=res/i;
}
}
sort(d,d+cnt);
for(int i=0;i<cnt;i++)
{
if(fast_pow(x, d[i],a/tmp)==1)
{
cout<<d[i]<<endl;
break;
}
}
}
return 0;
}
Diophantus of Alexandria
/*
x 、y、n都是正整数,并且 显然,x >= n , y >= n ,现在假设 y = n +k (k为正整数) ,
那么带入公式,可以得出 x = (n*(n+k))/k = n*n/k + n; 由于x 是正整数,
现在的关键问题就是要求出 n*n/ k 有多少组正整数的可能,显然,
所要求的就是 n*n 因子的个数// 问题已经非常接近答案了,但是最后还有一个问题,n<= 10^9 ,
那么n*n <= 10^18 ,对于一个这么大的数字怎样才能求出它因子的个数呢?
命题1: 一个正整数 n 可以用素因子唯一表示为 p1^r1 * p2^r2 * ... pk^rk (其中 pi 为素数)
, 那么这个数的因子的个数就是,(r1+1)*(r2+1)*...*(rk+1).
如果一个数字 n = p1^r1 * p2^r2 * ... pk^rk ,
那么 n*n = p1^r1 * p2^r2 * ... pk^rk * p1^r1 * p2^r2 * ... pk^rk ,
它的因子的个数就是 (2*r1+1)*(2*r2+1)*...*(2*rk+1).
由1/x+1/y=1/n
简化成(x-n)(y-n)=n*n,
很明显,只要能将n*n进行因数分解,
则其解都满足题意。
假设n可写成:
a1^k1 * a2^k2 * a3^k3 * ... * am^km, (1)
其中a1,a2,a3,...am互质,
则n*n的约数为
(2k1+1)(2k2+1)(2k3+1)...(2km+1), (2)
记其积为O,
从而其存在的解的个数为
[O+1]/2,
*/
#include<stdio.h>
#include<stdlib.h>
int prime( int num[] )//素数筛选
{
int hash[20000]={0};
for( int i=3; i<=200; i+=2 )
{
if( hash[i/2] )
continue;
int x=i<<1;
for( int j=i*i; j<40000; j+=x )
hash[j/2]=1;
}
int count=0;
num[++count]=2;
for( int i=1; i<20000; i++ )
{
if( hash[i]==0 )
num[++count]=(i<<1)+1;
}
return count;
}
int res( int num[], int count ,int number )
{
int sum=1;
for( int i=1; i<=count; i++ )
{
if( number==1 ) break;
int x=0;
while( number%num[i]==0 )
{
x++;
number/=num[i];
}
sum*=(2*x+1);
}
if( number!=1 )
sum*=3;
return sum;
}
int main()
{
int n,num[5000],number;
int count=prime( num );
scanf( "%d",&n );
for( int i=1; i<=n; i++ )
{
scanf( "%d",&number );
printf( "Scenario #%d:\n" ,i);
printf( "%d\n",(res( num,count,number )+1)/2 );
puts("");
}
return 0;
}
X问题
/*
该题是一道中国剩余定理题目,
刚开始我就用了一下暴力的方法 (同时也优化了一下)一下子就超时了,
后来看了一下这个题的解题报告才知道要用中国剩余定理,这也是我第一次用中国剩余定理解题。
这个定理又叫孙子定理。
就是给定几个数 虽然不互质 然后一个数取余他们又有相应的余数。
那么这个问题的答案相差的一定是这些数的最小公倍数。
首先我们求出最小公倍数K;
然后在N%k+1到N%k+k这个范围内暴找一下有没有一个符合所有条件的数,
我们把N分成一段段的,因为在每一段一定是没有或者只有一个的。为什么可以分成一段段的,
我们知道中国剩余定理求的是最小的数,那么它的倍数同样是符合条件的;
所以如果有的话 sum+=n/k;
不要忘记,然后判断在1到n%k这个范围内有没有符合条件的答案
( 因为我们是从数的最后面分段开始的,最前面可能还有数剩余 )。
*/
#include<stdio.h>
#include<stdlib.h>
int Gcd( int a, int b )
{
b==0?a:Gcd( b, a%b );
}
int judge( int a[], int b[], int n, int sum, int N )
{
int count=0;
int t=N%sum;
for( int i=t+1;i<=sum+t;i++ )
{
int cnt=0;
for( int j=0; j<n; j++ )
{
if( i%a[j]==b[j] )
cnt++;
else break;
}
if( cnt==n )
{
count+=N/sum;
break;
}
}
for( int i=1;i<=t; i++ )
{
int cnt=0;
for( int j=0; j<n; j++ )
{
if( i%a[j]==b[j] )
cnt++;
else break;
}
if( cnt==n )
{
count++;
break;
}
}
return count;
}
int main( )
{
int a[10],b[10];
int n,T,N;
scanf( "%d",&T );
while( T-- )
{
int sum=1;
scanf( "%d%d",&N, &n );
for( int i=0; i<n; i++ )
{
scanf( "%d",&a[i] );
int t=Gcd( sum , a[ i ] );
sum*=(a[i]/t);
}
for( int i=0; i<n; i++ )
scanf( "%d",&b[i] );
printf( "%d\n",judge( a,b,n,sum,N ) );
}
return 0;
}
Jacobi symbol
//a^((p-1)/2)%p==1,则有解
#include<cmath>
#include<iostream>
using namespace std;
const int maxn=1000000+10;
typedef long long ll;
bool not_pri[maxn];
int cnt=0,pri[maxn>>3];
void init()
{
for(int i=2;i<maxn;i++){
if(not_pri[i]) continue;
pri[cnt++]=i;
for(int j=i+i;j<maxn;j+=i) not_pri[j]=1;
}
}
ll pow(int a,int n,int p) //a^n%p
{
ll base=a,ret=1;
while(n){
if(n&1) ret=ret*base%p;
base=base*base%p;n>>=1;
}return ret;
}
int cal(int a,int p)
{
if(a%p==0) return 0;
return pow(a,p/2,p)==1?1:-1;
}
int main()
{
int a,n;init();
while(cin>>a>>n)
{
int ans=1;
if(not_pri[n])
{
for(int i=0;pri[i]<=n;i++)
{
if(n%pri[i]) continue;
int cnt=0;
while(n%pri[i]==0) n/=pri[i],cnt++;
int tmp=cal(a,pri[i]);
if(tmp==-1&&cnt%2==0) tmp=1;
ans*=tmp;
}
}
else ans=cal(a,n);
cout<<ans<<endl;
}
return 0;
}