bool isprime(int x)
{
if(x<=1)
return false;
for(int i=2;i<=x/i;i++)
{
if(x%i==0)
return false;
}
return true;
}
#include
using namespace std;
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
{
int p=0;
while(x%i==0)
{
x/=i;p++;
}
if(p)
cout<<i<<' '<<p<<endl;
}
if(x>1)
cout<<x<<" "<<1<<endl;
cout<<endl;
}
return 0;
}
基本原理:从小到大将每个质数的倍数筛去
若某个数没有被它前面的数筛掉,那么它一定是质数。
原因:它不是前面的2~p-1中任何一个数的倍数,那么它是质数
时间复杂度:n*log(log(n)),接近线性
const int N = 1e6+5;
bool isprime[N];
int prime[N];
int cnt;
void init(int n)
{
isprime[1]=true;
for(int i=2;i<=n;i++)
{
if(!isprime[i])
{
prime[++cnt]=i;
for(int j=i+i;j<=n;j+=i)
isprime[j]=true;
}
}
}
基本原理:每个数只会被它最小的质因数筛掉,那么每个数只会被筛一次,所以时间复杂度为o(n)
const int N = 1e6+5;
bool isprime[N];
int prime[N];
int cnt;
void init(int n)
{
isprime[1]=true;
for(int i=2;i<=n;i++)
{
if(!isprime[i])
prime[cnt++]=i;
for(int j=0;prime[j]<=n/i;j++)
{
isprime[prime[j]*i]=true;
if(i%prime[j]==0)
break;
}
}
}
一次检测中:
主要是把一个数n的n-1分解成d*2^ r的形式,其中d为奇数,正向过程是a^ n%p如果是1,就继续分解
a^ (n/2)%p,(a为一个与n互素的数)看是否为1,;如果是n-1就停止分解,说明至此无法判断是否为素数;如果不等于这两个值,则一定为合数。而在写代码过程是这个过程的逆向过程,先分解到底,看最后这个a^d%p是否为1或n-1,如果是说明已经分解到底了,也就是通过了此次素性测试。如果不是,说明在正向过程中出现了要么a的某次方为n-1,根据算法停止了检测过程;要么就是中间的某一个结果不等于这两个数,那么就是合数。就从最后往前面推,每一步看满不满足上述条件。直到判断为合数或者终止检测的那一步。
#include
using namespace std;
typedef long long ll;
ll q_mul(ll a,ll b,ll p)
{
ll ans=0;
while(b)
{
if(b&1)
ans=(ans+a)%p;
a=(a<<1)%p;
b>>=1;
}
return ans;
}
ll q_pow(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1)
ans=q_mul(ans,a,p);
a=q_mul(a,a,p);
b>>=1;
}
return ans;
}
bool Miller_Rabin(ll n){
if(n==2)return true;
if(n<2||!(n&1))return false;
int t=2,r=0;
ll m=n-1;
while(m%2==0){
r++;
m>>=1;
}
srand(100);
while(t--)
{
ll a=rand()%(n-1)+1;
ll x=q_pow(a,m,n),tmp=0;
for(int i=0;i<r;i++){
tmp=q_mul(x,x,n);
if(tmp==1&&x!=1&&x!=n-1)return false;
x=tmp;
}
if(tmp!=1)return false;
}
return true;
}
部分借鉴自 大神博客
void get_ans(int n)
{
vector<int> ans;
for(int i=1;i<=n/i;i++)
{
if(n%i==0)
{
ans.push_back(i);
if(i!=n/i)
ans.push_back(n/i);
}
}
sort(ans.begin(),ans.end());
for(auto x:ans) cout<<x<<" ";
cout<<endl;
}
原理:唯一分解定理
任意正整数 n = (a1 ^ p1) * (a2 ^ p2) * (a3 ^ p3) … * (ak ^ pk)
其中a1…ak均为质数
那么约数之和 sum=(p1+1) * (p2+1) * (p3+1) … (pk+1)
即对第一个质因子可以有p1+1种选法,一直到对第k个质因子,有pk+1种选法,把选中的数乘起来就是总约数个数
#include
#include
using namespace std;
typedef long long ll;
const int mod =1e9+7;
int main()
{
int n;
cin>>n;
unordered_map<int,int> mp;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
while(x%i==0)
{
x/=i;
mp[i]++;
}
if(x>1)
mp[x]++;
}
ll ans=1,ans2=1; //分别为约数个数,约数之和
for(auto x: mp)
{
int t=x.first,tt=x.second;
ll res=1;
while(tt--) res=(res*t+1)%mod;
ans=ans*res%mod;
ans2=ans2*(x.second+1)%mod;
}
cout<<ans<<" "<<ans2<<endl;
return 0;
}
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
因为gcd(a,b)=gcd(b,a%b)
为计算方便 , 递归的时候交换x,y位置,那么原式就变为
by+(a%b)x=gcd(a,b)
=>by + ( a - a / b (下取整) * b) * x = gcd(a,b)
=>ax + b(y - a / b * x) = gcd(a,b)
#include
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;y=0;
return a;
}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int x,y,a,b;
scanf("%d%d",&a,&b);
int r=exgcd(a,b,x,y);
printf("%d %d\n",x,y);
}
return 0;
}
若c%gcd(a,b)!=0那么无解
根据上面求出一组x0, y0满足a * x0 + b * y0 = gcd(a,b)
令d=c/gcd(a,b), t=c/d
ax0+by0=d
ab/d+ax0-ab/d+by0=d
a(x0+b/d)-b(y0-a/d)=d
两边同时乘以t, 即解得 x=(x0+b/d)*t, y=(y0-a/d)*t
#include
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;y=0;
return a;
}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;
}
int main()
{
int a,b,c,x0,y0,x,y;
scanf("%d%d%d",&a,&b,&c);
int d=exgcd(a,b,x0,y0);
if(c%d!=0)
printf("no solution\n");
else
{
int t=c/d;
x=(x0+b/d)*t;
y=(y0-a/d)*t;
printf("x=%d y=%d\n",x,y);
}
return 0;
}
题目链接
给定n组数据ai,bi,mi,对于每组数求出一个xi,使其满足ai∗xi≡bi(mod mi),如果无解则输出impossible。
输入格式
第一行包含整数n。
接下来n行,每行包含一组数据ai,bi,mi。
输出格式
输出共n行,每组数据输出一个整数表示一个满足条件的xi,如果无解则输出impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在int范围之内。
数据范围
1≤n≤105,
1≤ai,bi,mi≤2∗109
输入样例:
2
2 3 6
4 3 5
输出样例:
impossible
7
#include
using namespace std;
typedef long long ll;
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;y=0;
return a;
}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;
return r;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int a,b,m,x,y;
scanf("%d%d%d",&a,&b,&m);
int d=exgcd(a,m,x,y);
if(b%d)
printf("impossible\n");
else
{
printf("%d\n",(ll)x*(b/d)%m);
}
}
return 0;
}
typedef long long ll;
ll q_pow(int a,int b,int p)
{
ll ans=1;
while(b)
{
if(b&1) ans=(ll)ans*a%p;
b>>=1;
a=(ll)a*a%p;
}
return ans%p;
}
/*当你要计算 A^B%C的时候
如果B很大,达到10^100000,所以我们应该联想到降幂公式。
降幂公式:A^B%C = A^(B%phi(C) + phi(C))%C
分两种情况:
当B<=phi(C)时,直接用快速幂计算A^B mod C
当B>phi(C)时,用快速幂计算A^(B mod phi(C)+phi(C)) mod C
*/
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int mod = 1e9+7;
typedef long long ll;
ll phi(ll n) //求欧拉函数值
{
int ans=n,temp=n;
for(int i=2;i*i<=temp;i++)
{
if(temp%i==0)
{
ans-=ans/i;
while(temp%i== 0) temp/=i;
}
}
if(temp>1) ans-=ans/temp;
return ans;
}
ll mod_pow(ll x,ll n,ll mod) //快速幂
{
ll ans=1;
while(n)
{
if(n%2==1) ans=ans*x%mod;
x=x*x%mod;
n/=2;
}
return ans;
}
ll a,c;
char b[1000010];
int main()
{
while(scanf("%lld%s%lld",&a,b,&c)!=EOF)
{
c%=mod;
a%=mod;
ll phic=phi(mod);
int i,len=strlen(b);
ll res=0,ans;
for( i=0;i<len;i++)
{
res=res*10+b[i]-'0';
if(res>phic)
break;
}
if(i==len)
{
ans=mod_pow(a,res,mod)*c%mod;
}
else
{
res=0;
for(int i=0;i<len;i++)
{
res=res*10+b[i]-'0';
res%=phic;
}
ans=mod_pow(a,res+phic,mod)*c%mod;
}
cout<<ans<<endl;
}
//cout<<mod_pow(2,3,mod);
return 0;
}
以上模板转自:大神博客
费马小定理:如果p是一个质数,而整数a不是p的倍数,则有 a ^(p-1)≡1(mod p)
应用:(a / b) % p = a * q_pow( b, p-2 ) % p
根据定义求
//计算公式:phi[n]=n*(1-1/p1)*(1-1/p2)*...*(1-1/pn)=n*(p1-1)/p1*(p2-1)/p2*...(pn-1)/pn
int get_phi(int n)
{
int res=n;
for(int i=2;i<=n/i;i++)
{
if(n%i==0)
{
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1)
res=res/n*(n-1);
return res;
}
#include
using namespace std;
const int N =1e6+5;
typedef long long ll;
bool isprime[N];
int prime[N];
int phi[N];
int cnt;
void euler()
{
phi[1]=1;
for(int i=2;i<=N;i++)
{
if(!isprime[i])
{
prime[cnt++]=i;
phi[i]=i-1;
}
for(int j=0;prime[j]<=N/i;j++)
{
int t=prime[j]*i;
isprime[t]=true;
if(i%prime[j]==0)
{
phi[t]=prime[j]*phi[i];break;
}
phi[t]=(prime[j]-1)*phi[i];
}
}
}
int main()
{
euler();
return 0;
}
题目链接:Acwing 885.求组合数1
询问次数很大,所以需要预处理出来2000以内的所有c(a,b)
这里用到了一个递推式:c(a, b)=c(a-1,b-1)+c(a-1,b)
从前a个数里面挑b个数,它比a-1多的就是第a个数,那么如果b个数里面包含第a个数,就从前a-1个数里面挑b-1个数,如果不包含,就从前a-1个数里面挑b个数。分别对应等式右边的两个值。
#include
#include
#include
using namespace std;
const int mod=1e9+7;
const int N =2005;
int c[N][N];
void init()
{
for(int a=0;a<=2000;a++)
{
for(int b=0;b<=a;b++)
{
if(b==0)
c[a][b]=1;
else
c[a][b]=(c[a-1][b-1]+c[a-1][b])%mod;
}
}
}
int main()
{
int n;
cin>>n;
init();
while(n--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",c[a][b]);
}
return 0;
}
题目链接:Acwing 886. 求组合数2
由于询问数量同样很大,所以也需要进行预处理,只不过不是直接处理出c(a,b)的值。
这里用到了求组合数的定义式:
那么我们直接处理出1-N的阶乘就好。需要注意的是a / b != (a%mod) / (b%mod)
那么我们需要求出分母的逆元,那么需要处理出1-N阶乘的逆元。(由于mod是个质数,可以运用小费马定理求逆元)
#include
#include
#include
using namespace std;
typedef long long ll;
const int N =1e5+10,mod=1e9+7;
ll fac[N],infac[N];
ll q_pow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void init()
{
fac[0]=infac[0]=1;
for(int i=1;i<N-5;i++)
{
fac[i]=fac[i-1]*i%mod;
infac[i]=infac[i-1]*q_pow(i,mod-2)%mod;
}
}
int main()
{
init();
int n;
cin>>n;
while(n--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%lld\n",fac[a]*infac[a-b]%mod*infac[b]%mod);
}
return 0;
}
由于a,b非常大,而模数比较小,所以我们可以运用lucas定理求解。
lucas定理的内容是:c(a,b)%p = c(a%p, b%p) * c(a/p, b/p) %p
需要注意的是,如果a,b均小于p,那么可以直接运用定义求解
#include
#include
#include
using namespace std;
typedef long long ll;
ll p;
ll q_pow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
ll C(ll a,ll b)
{
ll ans=1;
for(ll i=a,j=1;j<=b;i--,j++)
{
ans=ans*i%p;
ans=ans*q_pow(j,p-2)%p;
}
return ans;
}
ll lucas(ll a,ll b)
{
if(a<p&&b<p)
return C(a,b);
return C(a%p,b%p)*lucas(a/p,b/p)%p;
}
int main()
{
int n;
cin>>n;
while(n--)
{
ll a,b;
cin>>a>>b>>p;
cout<<lucas(a,b)<<endl;
}
return 0;
}
题目链接:Acwing 888. 求组合数4
题目没有要求让模某一个数字,那么就要输出一个很大很大的数字,需要用高精度进行运算。
但是直接进行高精度乘法和除法比较慢,而且很麻烦。于是我们用分解质因数的方法进行计算。
计算出每一个质因数在c(a,b)的分子中出现了多少次,分母中出现了多少次,两者相减,就是它对答案的贡献,最后用高精度乘法把它们乘起来就可以了。
#include
#include
#include
#include
using namespace std;
const int N = 5005;
int prime[N],isprime[N],sum[N],cnt;
void init(int n)
{
for(int i=2;i<=n;i++)
{
if(!isprime[i])
{
prime[cnt++]=i;
for(int j=i+i;j<=n;j+=i)
isprime[j]=1;
}
}
}
int get_p(int n,int p)
{
int ans=0;
while(n)
{
ans+=n/p;
n/=p;
}
return ans;
}
vector<int> mul(vector<int> &a,int b)
{
int t=0;
vector<int> ans;
for(int i=0;i<a.size();i++)
{
t+=a[i]*b;
ans.push_back(t%10);
t/=10;
}
while(t)
{
ans.push_back(t%10);
t/=10;
}
return ans;
}
int main()
{
int a,b;
cin>>a>>b;
init(a);
for(int i=0;i<cnt;i++)
{
int p=prime[i];
sum[i]=get_p(a,p)-get_p(a-b,p)-get_p(b,p);
}
vector<int> ans;
ans.push_back(1);
for(int i=0;i<cnt;i++)
{
for(int j=0;j<sum[i];j++)
ans=mul(ans,prime[i]);
}
for(int i=ans.size()-1;i>=0;i--)
cout<<ans[i];
return 0;
}
// a[N][N]是增广矩阵
int gauss()
{
int c, r;
for (c = 0, r = 0; c < n; c ++ )
{
int t = r;
for (int i = r; i < n; i ++ ) // 找到绝对值最大的行
if (fabs(a[i][c]) > fabs(a[t][c]))
t = i;
if (fabs(a[t][c]) < eps) continue;
for (int i = c; i <= n; i ++ ) swap(a[t][i], a[r][i]); // 将绝对值最大的行换到最顶端
for (int i = n; i >= c; i -- ) a[r][i] /= a[r][c]; // 将当前上的首位变成1
for (int i = r + 1; i < n; i ++ ) // 用当前行将下面所有的列消成0
if (fabs(a[i][c]) > eps)
for (int j = n; j >= c; j -- )
a[i][j] -= a[r][j] * a[i][c];
r ++ ;
}
if (r < n)
{
for (int i = r; i < n; i ++ )
if (fabs(a[i][n]) > eps)
return 2; // 无解
return 1; // 有无穷多组解
}
for (int i = n - 1; i >= 0; i -- )
for (int j = i + 1; j < n; j ++ )
a[i][n] -= a[i][j] * a[j][n];
return 0; // 有唯一解
}
题目链接
给定一个整数n和m个不同的质数p1,p2,…,pm。
请你求出1~n中能被p1,p2,…,pm中的至少一个数整除的整数有多少个。
输入格式
第一行包含整数n和m。
第二行包含m个质数。
输出格式
输出一个整数,表示满足条件的整数的个数。
数据范围
1≤m≤16,
1≤n,pi≤109
输入样例:
10 2
2 3
输出样例:
7
#include
using namespace std;
typedef long long ll;
int a[20];
int main()
{
int n,m;
cin>>m>>n;
for(int i=0;i<n;i++)
cin>>a[i];
int ans=0;
for(int i=1;i<(1<<n);i++)
{
ll t=1,cnt=0;
for(int j=0;j<n;j++)
{
if(i>>j&1)
{
if(t*(ll)a[j]>m)
{
t=-1;break;
}
t*=a[j];cnt++;
}
}
if(t!=-1)
{
if(cnt%2)
ans+=m/t;
else
ans-=m/t;
}
}
cout<<ans<<endl;
return 0;
}