对于一个正整数n,它的欧拉函数的值即{p|p∈[1,n),p∈N+,p与n互质}的集合的大小,我们用φ(n)表示正整数n的欧拉函数
φ(n)=n(1-1/p1)(1-1/p2)……(1-1/pk),其中p1~pk为n的质因数
附证明:https://blog.csdn.net/paxhujing/article/details/51353672
1)如果n是素数,那么φ(n)=n-1,逆向也成立
2)如果n是素数,k是正整数,那么φ(n^k)=(n-1)*n^(k-1)
证明:显然一共有n^k-1个正整数,其中能被n^k整除的有n*t,t∈{1,2……n^(k-1)-1},所以有[(n^k)-1]-[n^(k-1)-1]个
3)如果m,n是互质的正整数,那么φ(mn)=φ(m)φ(n)
证明:m,n没有公共约数,我们令p1~pk1表示m的质因数,q1~qk2表示n的质因数,我们由计算公式可得:
φ(m)φ(n)=m(1-1/p1)(1-1/p2)……(1-1/pk1)n(1-1/q1)(1-1/q2)……(1-1/qk2)
φ(mn)=mn(1-1/p1)(1-1/p2)……(1-1/pk1)(1-1/q1)(1-1/q2)……(1-1/qk2)
4)n=p1^a1*p2^a2*……*pk^ak为n的素数幂分解(根据唯一分解定理),那么φ(n)=n(1-1/p1)(1-1/p2)……(1-1/pk)
证明:你们一定行!
由性质3:
φ(n)=φ(p1^a1)φ(p2^a2)……φ(pk^ak)
=(p1-1)*p1^(a1-1)……(pk-1)*pk^(ak-1)
=p1^a1*……*pk^ak*(1-1/p1)*……*(1-1/pk)
5)一个数n的质因数的和=φ(n)*n/2
题目大意:求φ(n)
模板题,不难想到利用性质4来求φ(n),显然我们需要进行质因数分解,代码:
#include
#define int long long
using namespace std;
int n;
int euler1(int m){//直接求
int ans=m;
for(int i=2;i<=m;i++){
if(m%i==0){
// ans=ans*(1-1/i)=>
// ans=ans-ans/i=>
ans=ans/i*(i-1);//防止溢出
while(m%i==0)m/=i;
}
}return ans;
}
int euler2(int m){//O(√n)
int ans=m;
for(int i=2;i*i<=m;i++){//任意一个合数都有不大于根号n的约数
if(m%i==0){
ans=ans/i*(i-1);
while(m%i==0)m/=i;
}
}if(m>1)ans=ans/m*(m-1);//最后的pk
return ans;
}
#define MAXN 100001
int euler[MAXN];
int euler3(int m){//筛法求euler函数O(n),假的,千万级就跑得很慢了
for(int i=1;i<=m;i++)euler[i]=i;
for(int i=2;i<=m;i++)if(euler[i]==i){//利用性质1
for(int j=i;j<=m;j+=i)euler[j]=euler[j]/i*(i-1);
}return euler[m];
}
main(){
while(~scanf("%lld",&n)&&n)cout<
题目大意:给定奇素数,求它的原根
原根是个数学符号,它的定理:n的原根为φ(φ(n)),当结论背下来吧,感兴趣的同学可以去网上找这方面的资料
题目大意:给定一个数n,需要我们求n与x不互质且x不为n的约数的个数(x<=n)
发现正面求解不太好搞,我们考虑逆向求解
对于第一个条件(求互质):ans1=n-φ(n)
对于第二个条件(求约数个数):我们由素数幂分解得到ans2=(a1+1)(a2+1)……(ak+1)个(详见性质4)
于是我们就得到了最终答案:ans=n-ans1-ans2个?
注意1又是n的约数且含在了φ(n)中,所以算了两次,因此我们要+1
#include
using namespace std;
#define MAXN 100001
int n,q,ans1,ans2,cnt[MAXN];
void euler(int m){
ans1=m,ans2=1;
for(int i=2;i*i<=m;i++){
if(m%i==0){
ans1=ans1/i*(i-1);
++q;
while(m%i==0)m/=i,++cnt[q];//求ak的大小
}
}
for(int i=1;i<=q;i++)ans2*=(cnt[i]+1);//+1?0次方,即不要它自己
if(m>1)ans1=ans1/m*(m-1),ans2*=2;//因为我们是取的根号,还有一半的约数个数没有取到
}
int main(){
scanf("%d",&n);
euler(n);
cout<
题目大意:给定一个数n,2~n的欧拉函数之和
去水题吧,注意要开longlong,而且数据貌似改了,不用特判n=1的
题目大意:见题面(有点长哇)
题目大意:优化如下代码
Int sum=0;
For(i,1,n-1)
For(j,i+1,n)sum+=gcd(i,j)
多组数据Case<=2e4,n<=1e6
对于一组Gcd(i,n),我们这样考虑:
Gcd(i*pi,n)=pi即Gcd(i,n/pj)=1,而我们可以求出满足Gcd(i,n/pj)=1的i有φ(n/pj)个,所以Gcd(i*pi,n)=pi也有φ(n/pj)个,那么对于一个单独n:(n)+=φ(n/pi)*pi,pi为n的因数
而对于此题,我们只需递推出所有状态就好了
#include
#define int long long
using namespace std;
#define MAXN 1000001
int r,n,q[MAXN],euler[MAXN];
void init(){
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",q+i);
for(int i=1;i<=n;i++)r=max(r,q[i]);
}
void calc(){
for(int i=1;i<=r;i++)euler[i]=i;
for(int i=2;i<=r;i++)if(euler[i]==i){
for(int j=i;j<=r;j+=i)euler[j]=euler[j]/i*(i-1);
}
}
int f[MAXN];
void solv(){//对于每一个询问n+=euler[pi]*pi
for(int pi=1;pi<=r;pi++)//公约数大小
for(int n_pi=2;n_pi<=r/pi;n_pi++)f[pi*n_pi]+=euler[n_pi]*pi;//注意n/pi不能为本身,因为没有意义
//gcd(x,n)=pi
//gcd(x/pi,n/pi)=1
for(int i=1;i<=r;i++)f[i]+=f[i-1];
for(int i=1;i<=n;i++)cout<
题目大意:给定一个整数n,求sum gcd(i,n)(1<=i<=n)
上一道题都成功解决,这一道题就不说了,只是注意n∈(0,2^32),因此不能开数组,而应该实时计算(见第二种写法)
对于整数a,p,若存在一个整数x,使ax≡1 (mod p)即ax%p=1成立,则称x是a对p的逆元(当且仅当gcd(a,p)=1时,才存在且存在唯一逆元),我们用inv(a)表示x
模意义下除法不能直接操作,因此要引入逆元(还有加法逆元等,一般指乘法逆元)
·费马小定理:若p是质数且gcd(a,p)=1,则a^(p-1)≡1 (mod p)
显然我们有a*a^(p-2)≡1 (mod p),即inv(a)=a^(p-2),用快速幂计算inv(a),复杂度log2(a),当模数p是质数时,用费马求解
·欧拉定理:若gcd(a,p)=1,则a^(φ(p))≡1(mod p)
显然此时inv(a)=a^(φ(p)-1),使用欧拉函数+快速幂计算,复杂度同费马,但较费马而言,模数p可以不为质数,因此应用范围广一点
·扩展欧几里得:若gcd(a,b)=1,则ax+by=1
这个不定方程的解x就是a对b的逆元,y就是b对a的逆元
证明:显然ax%b+by%b=1%b=>ax%b=(1%b)=1=>ax≡1 (mod b)
·线性递推:当p是质数的时候,inv(a)=(p-p/a)*inv(p%a)%p
证明:设x=p%a,y=p/a,则有:
x+ay=p=>
(x+ay)%p=0=>
x*inv(a)%p=(p-y)%p=>
inv(a)=(p-y)*inv(x)%p=>
inv(a)=(p-p/a)*inv(p%a)%p
#include
#define int long long
using namespace std;
int n,p;
struct FEIMA{
inline int pow_mod(int a,int b,int p){//a的b次方求余p快速幂
int ret=1;
while(b){
if(b&1)ret=(ret*a)%p;
a=(a*a)%p;
b>>=1;
}return ret;
}
inline int FeiMaX(int a,int p){//费马求a关于b的逆元
return pow_mod(a,p-2,p);
}
inline void solv(){
for(int i=1;i<=n;i++)cout<>=1;
}return ret;
}
inline int OuLa(int a,int p){
return pow_mod(a,Eulerp-1,p);
}
inline int euler(int m){
int ans=m;
for(int i=2;i*i<=m;i++){
if(m%i==0){
ans=ans/i*(i-1);
while(m%i==0)m/=i;
}
}if(m>1)ans=ans/m*(m-1);
return ans;
}
inline void solv(){
Eulerp=euler(p);
for(int i=1;i<=n;i++)cout<
学了乘法逆元,我们就能ac前面的几道题了!
题目大意:给定正整数n,m,p,求(0,n!]中所有与m!互质的数的个数%p(数据默认n>m)
我们还是先研究局部(因为n!必然能够被m!整除),对于一个区间m!,如果找到了一个数p与m!互质,那么,p+m!,p+2*m!,……,p+(n!/m!-1)*m!都与p是相同性质的,因此我们的答案即ans(m!)*(n!/m!)即φ(m!)*(n!/m!),我们将它展开得到:n!*(1-1/p1)……(1-1/pk)=>n!*[(p1-1)/p1]……[(pk-1)/pk],又因为n,m<=10000000而且内存限制足够大,暴力预处理即可
/*
暴力处理n!:n数组
处理1-1/pi:inv数组(逆元),key数组(表示(1-1/p1)……(1-1/pk))
对于每个key[m],我们这样考虑:
当m为素数时,显然key[m]=key[m-1]*(1-1/m)
当m不为素数,我的m肯定能被分成若干小于m的合数积,因此key[m]=key[m-1]
*/
#include
using namespace std;
#define N 10000000
#define int long long
int cas,p;
int key[N+10];
int inv[N+10];
int cnt,prime[N+10];
bool vst[N+10];
inline void prepP(){//欧拉素数筛
for(int i=2;i<=N;i++){
if(!vst[i])prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=N;j++){
vst[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
inline void prepVK(){
inv[1]=1;
for(int i=2;i<=N;i++)inv[i]=(p-p/i)*inv[p%i]%p;//逆元
key[1]=1;
for(int i=2;i<=N;i++){
key[i]=key[i-1];//
if(!vst[i])key[i]=key[i]*(i-1)%p*inv[i]%p;
}
}
int n[N+10];
inline void prepN(){
n[1]=1;
for(int i=2;i<=N;i++)n[i]=n[i-1]*i%p;
}
main(){
scanf("%lld%lld",&cas,&p);
prepP();
prepVK();
prepN();
while(cas--){
int x,y;scanf("%lld%lld",&x,&y);
printf("%lld\n",n[x]*key[y]%p);
}
}
题目大意:看题目……
好题,我们很容易想到答案即φ(TTr/TT/(l-1)),即TT(l~r)*(1-1/p1)*(1-1/p2)……(1-1/pk),我们又注意到总共只有60个质因数……
#include
#define ll long long
using namespace std;
inline int get(){register int re=0,f=1;register char c;while(c=getchar(),(c>='0'&&c<='9')^1)f=c^'-';while(re=(re<<1)+(re<<3)+(c^48),c=getchar(),(c>='0'&&c<='9'));return f?re:-re;}
#define N 100000
#define p 19961993
#define FOR(i,L,R) for(register int i=(L);i<=(R);++i)
bool vst[287];
int inv[287],prime[67];
struct Seg{
ll c[N*4];
bitset<61>s[N*4];
#define ls v<<1
#define rs v<<1|1
inline void cull(int v,int k){
c[v]=k;
s[v]=0;
FOR(i,1,60)if(k%prime[i]==0)s[v][i]=1;
}
inline void maintain(int v){
c[v]=c[ls]*c[rs]%p;
s[v]=s[ls]|s[rs];
}
inline void Modify(int v,int L,int r,int x,int k){
if(L>x||r>1;
Modify(ls,L,Mid,x,k),Modify(rs,Mid+1,r,x,k);
maintain(v);
}
inline pair > query(int v,int L,int r,int a,int b){
if(L>b||r>1;
pair >Left=query(ls,L,Mid,a,b);
pair >Right=query(rs,Mid+1,r,a,b);
return make_pair(Left.first*Right.first%p,Left.second|Right.second);
}
inline void build(int v,int L,int r){
if(L==r)return cull(v,3),void();
int Mid=L+r>>1;
build(ls,L,Mid),build(rs,Mid+1,r);
maintain(v);
}
}t;
inline void prep(){
FOR(i,2,281){
if(!vst[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&i*prime[j]<=281;j++){
vst[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}inv[1]=1;
FOR(i,2,281)inv[i]=(ll)(p-p/i)*inv[p%i]%p;
}
inline void GetAns(int a,int b){
pair >ans=t.query(1,1,N,a,b);
FOR(i,1,60)if(ans.second[i])ans.first=(ll)ans.first*(prime[i]-1)*inv[prime[i]]%p;
cout<
using namespace std;
#define N 100000
#define p 19961993
#define int long long
#define FOR(i,L,R) for(register int i=(L);i<=(R);i++)
bool vst[290];
int prime[61],inv[290];
int BIT[61]={1};
inline void prep(){
FOR(i,1,60)BIT[i]=BIT[i-1]<<1;
FOR(i,2,281){
if(!vst[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&i*prime[j]<=281;j++){
vst[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}inv[1]=1;
FOR(i,2,281)inv[i]=(p-p/i)*inv[p%i]%p;
// int ans=0;FOR(i,1,61)ans+=BIT[i];cout<b||r>1;
return f?query(f,v<<1,L,Mid,a,b)|query(f,v<<1|1,Mid+1,r,a,b):query(f,v<<1,L,Mid,a,b)*query(f,v<<1|1,Mid+1,r,a,b)%p;
}
inline void Modify(int f,int v,int L,int r,int x,int k){
if(L>x||r>1;
Modify(f,v<<1,L,Mid,x,k),Modify(f,v<<1|1,Mid+1,r,x,k);
maintain(f,v);
}
inline void build(int f,int v,int L,int r){
if(L==r)return cull(f,v,3),void();
int Mid=L+r>>1;
build(f,v<<1,L,Mid);
build(f,v<<1|1,Mid+1,r);
maintain(f,v);
}
}t;
inline void GetAns(int a,int b){
int ans1=t.query(0,1,1,N,a,b),ans2=t.query(1,1,1,N,a,b);
for(int i=1;i<=60;i++)
if(ans2&BIT[i])ans1=ans1*(prime[i]-1)*inv[prime[i]]%p;
cout<
题目大意:见题面……
思路?代码?我不会……