欧拉函数

1.SDOI2008仪仗队

这里写图片描述
题意:求 nn n ∗ n 的矩阵中左下点能直接看到的点的个数
n<=40000 n <= 40000
注意到每条不重合的射线上最多只有一个点能被看见
记左下角为 (0,0) ( 0 , 0 )
那么对于同一射线上的点 (x1,y1) ( x 1 , y 1 ) (x2,y2) ( x 2 , y 2 )
他们的斜率 y1x1=y2x2 y 1 x 1 = y 2 x 2 都能被约分成互质两数的比值 yx y x
gcd(x,y)=1 g c d ( x , y ) = 1 的点的个数就是答案
当n>=2时
容易发现 x x 轴和y轴与对角线上一共有3个点
这三条线把矩阵分成了两个关于对角线对称的三角形
而左上三角形的横坐标总是小于等于纵坐标
故我们可以用欧拉函数求得左上三角形中每一行横坐标与纵坐标最大公约数为1的点数
如当y=2 左上三角形这一行对答案的贡献为 ϕ(2)=1 ϕ ( 2 ) = 1 总贡献就为2
提前筛出n以内的欧拉函数值

#include<cstdio>
#include<cstring>
using namespace std;
int p[40007],n;
void getphi(int *phi){
    for(int i=2;i<=n;i++){
        phi[i]=0;
    }
    phi[1]=1;
    for(int i=2;i<=n;i++) 
    if(!phi[i]){//质因数 处理所有该数的倍数(乘上(1-1/i) 即
    //乘上((i-1)/i)
        for(int j=i;j<=n;j+=i){
            if(!phi[j]) phi[j]=j;//初始化phi[j] 
            phi[j]=phi[j]/i*(i-1);//乘上质因数i时的(1-1/i)
        }
    }
}
int res=0;
int main(){
    scanf("%d",&n);
    if(n==1) {printf("0");return 0;}
    else if(n==2) {printf("3");return 0;}
    getphi(p);
    res=3;
    for(int i=2;i<n;i++){
        res+=p[i]*2;
    }
    printf("%d",res);
    return 0;
}

2.SDOI2012 longge的问题

gcd(i,n)==d g c d ( i , n ) == d 可以化成 gcd(i/d,n/d)==1 g c d ( i / d , n / d ) == 1 n==0 (mod d) n == 0   ( m o d   d )
然后就是求欧拉函数

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
#define ll long long
ll phi(ll x){
    ll res=x,top=std::sqrt(x);
    for(ll i=2;i<=top;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;
}
/*
所有gcd(i,n)==1 的和是phi(n)
然后gcd(i,n)==x&&n%x==0时 => 
对答案的贡献为gcd(i/x,n/x)==1
*/
int main(){
    ll n;
    std::cin>>n;
    ll ans=0,top=std::sqrt(n);
    for(ll i=1;i<=top;i++){
        if(n%i==0)
            ans+=i*phi(n/i)+n/i*phi(i);
    }
    //注意不要漏掉完全平方的情况
    if(top*top==n) ans-=phi(top)*top;
    printf("%lld\n",ans);
    return 0;
}   

3.上帝与集合的正确用法

拓展欧拉定理:

ab mod p =ab modϕ(p) +ϕ(p)mod p a b   m o d   p   = a b   m o d ϕ ( p )   + ϕ ( p ) m o d   p

当且仅当
(b>=ϕ(p)) ( b >= ϕ ( p ) )

用这个式子递归求解即可


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,P;
#define ll long long
ll phi(int x){
  ll res=x;
  for(int 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;
}
ll ksm(ll a,int b,int mod){
  ll res=1;
  while(b){
    if(b&1) res=res*a%mod;
    a=a*a%mod;
    b>>=1;
  }
  return res;
}
int solve(int p){
  if(p==1) return 0;
  return ksm(2,solve(phi(p))+phi(p),p);
}

int main(){
  scanf("%d",&T);

  while(T--){
    scanf("%d",&P);
    printf("%d\n",solve(P));
  }
  return 0;
}

附:欧拉函数的线性筛法

void get_phi(int *phi){//线性筛欧拉函数
    phi[1] = 1;
    for (int i = 2; i <= maxn;i++){
        if(!phi[i]){
            phi[i] = i - 1;
            prime[++tot] = i;
        }
        for (int j = 1; j <= tot;j++){
            if(i*prime[j]>maxn)
                break;
            if(!(i%prime[j])){
                //特殊性质1:i%p==0 p为质数 phi(i*p)=p*phi(i)
                phi[i * prime[j]] = prime[j] * phi[i];
                break;
                //保证只被最小的质因数筛去一次
            }else{
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);
                //非完全积性函数 当i⊥j phi(i*j)=phi(i)phi(j);
                //素数的欧拉函数值为其本身值-1
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值