zoukankan      html  css  js  c++  java
  • 【暖*墟】#数论# 欧拉函数的学习与练习

    欧拉函数的定义

    ϕ(n):对于整数n,小于等于n、且与n互质的正整数的个数。

    欧拉函数的计算方法

     √n计算单值欧拉函数:计算ϕ(n),分情况讨论。

    1.当n=1时,很明显,答案为1。

    2.当n为质数时,根据素数的定义,答案为n−1。

    3.当n为合数时,对n进行质因数分解:设n=a1^p1∗a2^p2...∗ak^pk,

    (1)假设k=1,即n=a1^p1、只有一个因数就是素数a1。

    • 那么:ϕ(p^k)=p^k−p^(k−1)。
    • 证明:容斥原理知,答案=n-与它不互素的数的个数,
    • p是素数,在p^k中与其不互素的数为1∗p,2∗p....p^(k−1)∗p,共p^(k−1)个。

    (2)当k≠1时,ϕ(n)=ϕ(a1^p1∗a2^p2...∗ak^pk)。

    • 通过k=1求得的性质可知:

    ϕ(n) = ∏(i=1~k)ai^Pi−ai^(Pi−1)

            = ∏(i=1~k)(ai^Pi)*(1-1/ai)

            = n∗∏(i=1~k)(1-1/ai)

            = n∗∏(i=1~k)((ai-1)/ai);

    int euler(int x){
        int ans=x;
           for(int i=2;i*i<=x;i++)
            if(x%i==0){
                ans=ans/i*(i-1);
                while(x%i==0) x/=i;
        } if(x>1) ans=ans/x*(x-1);
        return ans; //返回ϕ(x)的值
    }

    欧拉函数的性质

    1.ϕ(n)为积性函数。即:gcd(a,b)=1时,ϕ(ab)=ϕ(a)*ϕ(b)。

    积性函数:若一个定义在正整数域上的函数f(x),

    对于任意满足gcd(x,y)==1的x、y都有f(xy)=f(x)∗f(y)。

    常见积性函数:莫比乌斯函数μ(n),欧拉函数ϕ(n),

    一个数n的约数个数d(n),一个数n的约数和σ(n),f(x)=x^k(k∈N)......

    证明欧拉函数是积性函数:

    狄利克雷卷积:f(x),g(x) 都是积性函数,则狄利克雷卷积 h(x)=∑(d|x)f(d)*g(x/d)

    • 性质:若f(x),g(x)都是积性函数,则狄利克雷卷积也是积性函数。

    积性函数的性质:任意积性函数都可以线性筛(在严格O(n)时间复杂度内筛出)。

    2.∑(d|n)ϕ(d)=n。

    3.1到n中与n互质的数的和为n∗ϕ(n)/2 (n>1)。

    • 证明:若gcd(n,i)=1,那么gcd(n,n−i)=1。
    • 即,与n互质的数都是成对出现的。且每一对的和都为n。

    4. a^(ϕ(n))≡1(modn)。

    欧拉函数的线性筛法

    利用三个性质:

    性质1 若p为素数,则φ(p)=p−1。

    性质2 若i mod p≠0 ,且p为素数,则φ(i∗p)=φ(i)∗φ(p)=φ(i∗p)=φ(i)∗(p−1)。

    性质3 若i mod p=0 ,且p为素数,则φ(i∗p)=φ(i)∗p。详情见 这里

    int phi[MAXN],vis[MAXN],prime[MAXN],tot=0;
    
    void GetPhi(int n){
        phi[1]=1; //特例:ϕ(1)=1;
        for(int i=2;i<=n;i++){
            if(!vis[i]) prime[++tot]=i,phi[i]=i-1; //i是素数(1)
            for(int j=1;j<=tot&&i*prime[j]<=n;j++){ //枚举“i*质数”
                vis[i*prime[j]]=1; //标记“i*质数”为合数
                if(i%prime[j]==0) //(3)注意这里可以直接break;
                 {phi[i*prime[j]]=phi[i]*prime[j];break;}
                else phi[i*prime[j]]=phi[i]*(prime[j]-1); }//(2)
        } for(int i=1;i<=n;i++) cout<<phi[i]<<endl;
    }

    欧拉函数与原根

    概念引入:由费马小定理可知,如果a于p互质,则有a^(p-1)≡1(mod p)。

    对于任意的a是不是一定要到p-1次幂才会出现上述情况呢?显然不是。

    p确定,对于某个a值,当第一次出现a^k≡1(mod p)时, 记为ep(a)=k。

    如果k=p-1,称a是p的原根。--> 每个素数恰好有φ(p-1)个原根(φ(x)为欧拉函数)。

    • 定理:原根个数为φ(φ(m));对于质数m,由于φ(m)=m-1,所以为φ(m-1)。
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // poj1284 Primitive Roots //原根与欧拉函数
    
    // 【题意】 求奇素数的原根的个数。
    
    // 原根个数为φ(φ(m));对于奇素数m,由于φ(m)=m-1,所以为φ(m-1)。
    
    int euler(int x){
        int ans=x;
           for(int i=2;i*i<=x;i++)
            if(x%i==0){
                ans=ans/i*(i-1);
                while(x%i==0) x/=i;
        } if(x>1) ans=ans/x*(x-1);
        return ans; //返回ϕ(x)的值
    }
    
    int main(){ int x; while(scanf("%d",&x)!=EOF) cout<<euler(x-1)<<endl; }
    poj1284 Primitive Roots //原根与欧拉函数
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // CF284A Cows and Primitive //原根与欧拉函数
    
    // 【题意】 求素数的原根的个数。
    
    // 原根个数为φ(φ(m));对于素数m,由于φ(m)=m-1,所以为φ(m-1)。
    
    int euler(int x){
        int ans=x;
           for(int i=2;i*i<=x;i++)
            if(x%i==0){
                ans=ans/i*(i-1);
                while(x%i==0) x/=i;
        } if(x>1) ans=ans/x*(x-1);
        return ans; //返回ϕ(x)的值
    }
    
    int main(){ int x; scanf("%d",&x); cout<<euler(x-1)<<endl; }
    CF284A Cows and Primitive //原根与欧拉函数

    欧拉函数的相关习题

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // UVA10299 Relatives
    // 多组数据,每组计算给定数的欧拉函数。
    
    int euler(int x){
        int ans=x;
           for(int i=2;i*i<=x;i++)
            if(x%i==0){
                ans=ans/i*(i-1);
                while(x%i==0) x/=i;
        } if(x>1) ans=ans/x*(x-1);
        return ans; //返回ϕ(x)的值
    }
    
    int main(){ int x; while(scanf("%d",&x)&&x)
       { if(x==1) cout<<0<<endl; else cout<<euler(x)<<endl; } }
    UVA10299 Relatives //【模板】单值欧拉函数
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // UVA10179 Irreducable
    // 多组数据,每组计算给定数的欧拉函数。
    
    int euler(int x){
        int ans=x;
           for(int i=2;i*i<=x;i++)
            if(x%i==0){
                ans=ans/i*(i-1);
                while(x%i==0) x/=i;
        } if(x>1) ans=ans/x*(x-1);
        return ans; //返回ϕ(x)的值
    }
    
    int main(){ int x; while(scanf("%d",&x)&&x) cout<<euler(x)<<endl; }
    UVA10179 Irreducable //【模板】单值欧拉函数
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // poj2773 Happy 2006 //求第k个与n互素的数 
    
    // gcd(t×n+a,n)=gcd(a,n),所有与n互质的数都以phi(n)为周期。
    
    ll euler(ll x){
        ll ans=x;
        for(ll i=2;i*i<=x;i++)
            if(x%i==0){
                ans=ans/i*(i-1);
                while(x%i==0) x/=i;
        } if(x>1) ans=ans/x*(x-1);
        return ans; //返回ϕ(x)的值
    }
    
    ll gcd(ll a,ll b){
        if(b==0) return a;
        else return gcd(b,a%b);
    }
    
    ll n,k,phi,p[1000019]; 
    
    int main(){ 
        while(scanf("%d%d",&n,&k)!=EOF){ 
            phi=0; for(int i=1;i<=n;i++)
                if(gcd(i,n)==1) p[++phi]=i;
            printf("%d
    ",p[(k-1)%phi+1]+(k-1)/phi*n);
        }
    }
    poj2773 Happy 2006 //求第k个与n互素的数
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // UVA12995 Farey Sequence //求欧拉函数的前缀和
    
    // 求∑(i=2~n)ϕ(i),用欧拉函数的线性求法即可.
    
    const ll N=1000019;
    
    ll phi[N],vis[N],prime[N],p_num=0,n,sum[N];
    
    void GetPhi(ll n){
        //phi[1]=1; //特例:ϕ(1)=1; //此题=1时输出0
        for(ll i=2;i<=n;i++){
            if(!vis[i]) prime[++p_num]=i,phi[i]=i-1; //i是素数(1)
            sum[i]=sum[i-1]+phi[i]; //前缀和
            for(ll j=1;j<=p_num&&i*prime[j]<=n;j++){ //枚举“i*质数”
                vis[i*prime[j]]=1; //标记“i*质数”为合数
                if(i%prime[j]==0) //(3)注意这里可以直接break;
                 {phi[i*prime[j]]=phi[i]*prime[j];break;}
                else phi[i*prime[j]]=phi[i]*(prime[j]-1); }//(2)
        }
    }
    
    int main(){ GetPhi(1000001); 
        while(scanf("%lld",&n)&&n) printf("%lld
    ",sum[n]); }
    UVA12995 Farey Sequence //求欧拉函数的前缀和
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // SP5971 LCMSUM //欧拉函数求LCM的前缀和
    
    // 答案为(1/2)*∑(d′|n)(d′×φ(d′)+n)。
    // 预处理phi数组,再枚举每个约数去计算对所有倍数的贡献。
    
    const ll N=1000019;
    
    ll phi[N],vis[N],prime[N],p_num=0,n,ans[N];
    
    void GetPhi(ll n){
        phi[1]=1; //特例:ϕ(1)=1;
        for(ll i=2;i<=n;i++){
            if(!vis[i]) prime[++p_num]=i,phi[i]=i-1; //i是素数(1)
            for(ll j=1;j<=p_num&&i*prime[j]<=n;j++){ //枚举“i*质数”
                vis[i*prime[j]]=1; //标记“i*质数”为合数
                if(i%prime[j]==0) //(3)注意这里可以直接break;
                 {phi[i*prime[j]]=phi[i]*prime[j];break;}
                else phi[i*prime[j]]=phi[i]*(prime[j]-1); }//(2)
        } for(int i=1;i<=n;i++) //枚举因数i的所有倍数
            for(int j=i;j<=n;j+=i) ans[j]+=i*phi[i]/2;
        for(int i=1;i<=n;i++) ans[i]=ans[i]*i+i; //处理公式中的+n
    }
    
    int main(){ GetPhi(1000001); int T; cin>>T; 
        while(T--) scanf("%lld",&n),printf("%lld
    ",ans[n]); }
    SP5971 LCMSUM //欧拉函数求LCM的前缀和

     

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <iomanip>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    
    // p1447 能量采集 //欧拉函数 + 数学计算
    
    // 可以发现:(0,0)到(x,y)上的整数点个数(包括点(x,y))为gcd(x,y)。
    
    // 那么答案为:2∑(gcd(i,j)-1)+nm=2∑gcd(i,j)-nm。
    
    // 根据:https://images2015.cnblogs.com/blog/1078180/201706/1078180-20170613101314712-47533424.png
    // 可得:∑∑gcd(i,j) = ∑∑ ( ∑(d|gcd(i,j))*phi(d) ) [ 根据:∑(d|n)ϕ(d)=n ]
    //          = ∑∑ ( ∑(d|i&d|j))*phi(d) ) = ...
    
    const int N=100019;
    
    int phi[N],vis[N],prime[N],tot=0; ll sum[N];
    
    void GetPhi(int n){
        phi[1]=sum[1]=1; //特例:ϕ(1)=1;
        for(int i=2;i<=n;i++){
            if(!vis[i]) prime[++tot]=i,phi[i]=i-1; //i是素数(1)
            for(int j=1;j<=tot&&i*prime[j]<=n;j++){ //枚举“i*质数”
                vis[i*prime[j]]=1; //标记“i*质数”为合数
                if(i%prime[j]==0) //(3)注意这里可以直接break;
                 {phi[i*prime[j]]=phi[i]*prime[j];break;}
                else phi[i*prime[j]]=phi[i]*(prime[j]-1); } //(2)
            sum[i]=sum[i-1]+phi[i];
        }
    }
    
    ll cal(int a,int b){ int lastt; ll ans=0;
        for(int i=1;i<=a&&i<=b;i=lastt+1) //调和级数の分块法
          lastt=min(a/(a/i),b/(b/i)), //(快速确定相同的a/i和b/i的sum值)
          ans+=(ll)(sum[lastt]-sum[i-1])*(a/i)*(b/i); return ans; }
    
    int main(){   
        GetPhi(100001); int a,b; scanf("%d%d",&a,&b);
        printf("%lld
    ",2*cal(a,b)-(ll)a*b);
    }
    p1447 能量采集 //欧拉函数 + 复杂数学计算

    欧拉定理与相关习题

    欧拉定理

    扩展欧拉定理:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // p5091 【模板】 扩展欧拉定理 // 求a^b mod m 
    
    //【扩展欧拉定理】 b≥φ(m)时,a^b≡a^((bmodφ(m))+φ(m)) mod m ;
    
    int a,b,m,ans=1; bool flag;
    
    int euler(int x){
        int ans=x;
           for(int i=2;i*i<=x;i++)
            if(x%i==0){
                ans=ans/i*(i-1);
                while(x%i==0) x/=i;
        } if(x>1) ans=ans/x*(x-1);
        return ans; //返回ϕ(x)的值
    }
    
    int main(){
        char c; scanf("%d%d",&a,&m); int phi=euler(m);
        while(!isdigit(c=getchar())); //边读入b边取模
            for(;isdigit(c);c=getchar()){
                b=b*10+c-'0'; if(b>=phi) flag=true,b%=phi;
        } if(flag) b+=phi; //只有b>=phi时,公式才成立(否则不用+φ(m))
        for(int i=20;i>=0;i--){ //ksm求a^((bmodφ(m))+φ(m))
            ans=1ll*ans*ans%m;
            if(b&(1<<i)) ans=1ll*ans*a%m;
        } cout<<ans<<endl; return 0;
    }
    p5091 【模板】 扩展欧拉定理 // 求a^b mod m
    // luogu-judger-enable-o2
    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<string>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    // p4139 上帝与集合的正确用法 //扩展欧拉定理求2^(2^(2^(2...)))mod p
    
    // 【扩展欧拉定理】 b≥φ(p))时,a^b≡a^((bmodφ(p)))+φ(p))) mod p ;
    // 令f(p)=2^(2^(2^(2...))) mod p,则有f(p)=2^(f(φ(p))+φ(p)) mod p 。
    
    const ll N=10000019;
    
    ll phi[N],prime[N],p_num=0,p;
    
    void GetPhi(ll n){
        phi[1]=1; //特例:ϕ(1)=1;
        for(ll i=2;i<=n;i++){
            if(!phi[i]) prime[++p_num]=i,phi[i]=i-1; //i是素数(1)
            for(ll j=1;j<=p_num&&i*prime[j]<=n;j++){ //枚举“i*质数”
                if(i%prime[j]==0) //(3)注意这里可以直接break;
                 {phi[i*prime[j]]=phi[i]*prime[j];break;}
                else phi[i*prime[j]]=phi[i]*(prime[j]-1); }//(2)
        }
    }
    
    ll ksm(ll a,ll b,ll mod){ //快速幂
        ll anss=1; while(b) anss=anss*(b&1?a:1)%mod,
            a=a*a%mod,b>>=1; return anss;
    }
    
    ll solve(ll p){ if(p==1) return 0;
        return ksm(2,solve(phi[p])+phi[p],p); } //递归求解函数
    
    int main(){
        GetPhi(10000001); int T; cin>>T; 
        while(T--) scanf("%lld",&p),printf("%lld
    ",solve(p));
    }
    p4139 上帝与集合的正确用法 //扩展欧拉定理求2^(2^(2^(2...)))mod p
    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <iomanip>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    
    //【uva10692】Huge Mods //欧拉定理
    
    //计算a1^a2^a3^a4......^an模m的值。
    
    //欧拉定理:(a^x)%m=(a^(x%phi(m)+phi(m))%m x>=phi(m)
     
    const int N=100019;
    int m,n,a[N];
    char M[15];
     
    int phi[N],vis[N],prime[N],tot=0;
    
    void GetPhi(int n){
        phi[1]=1; //特例:ϕ(1)=1;
        for(int i=2;i<=n;i++){
            if(!vis[i]) prime[++tot]=i,phi[i]=i-1; //i是素数(1)
            for(int j=1;j<=tot&&i*prime[j]<=n;j++){ //枚举“i*质数”
                vis[i*prime[j]]=1; //标记“i*质数”为合数
                if(i%prime[j]==0) //(3)注意这里可以直接break;
                 {phi[i*prime[j]]=phi[i]*prime[j];break;}
                else phi[i*prime[j]]=phi[i]*(prime[j]-1); }//(2)
        }
    }
    
    int pow_mod(int x,int k,int mod){
        int now=1; for(int i=0;i<k;i++){
            if(x==1) break; now*=x; if(now>=mod) break;
        } if(now>=mod) now=mod; else now=0;
        if(k==0) return 1; int ans=pow_mod(x*x%mod,k>>1,mod);
        if(k&1) ans=ans*x%mod; return ans+now;
    }
     
    int dfs(int i,int mod){
        if(i==n-1){ if(a[i]>=mod) return a[i]%mod+mod; return a[i]; }
        int k=dfs(i+1,phi[mod]); return pow_mod(a[i],k,mod);
    }
     
    int main() {
        GetPhi(100001); int kase=0;
        while(~scanf("%s",M)&&M[0]!='#'){
            sscanf(M,"%d",&m); scanf("%d",&n);
            for(int i=0;i<n;i++) scanf("%d",&a[i]);
            printf("Case #%d: %d
    ",++kase,dfs(0,m)%m); 
        }
    }
    uva10692 Huge Mods //欧拉定理+递归
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<queue>
    #include<map>
    #include<vector>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    
    /*【p3747】相逢是问候 */
    
    //【标签】欧拉函数 + 线段树 + 极限标记思想(还是过不了...)
    
    void reads(ll &x){ //读入优化(正负整数)
        ll fx=1;x=0;char ch_=getchar();
        while(ch_<'0'||ch_>'9'){if(ch_=='-')fx=-1;ch_=getchar();}
        while(ch_>='0'&&ch_<='9'){x=x*10+ch_-'0';ch_=getchar();}
        x*=fx; //正负号
    }
    
    const ll N=500019,max_phi=150019;
    
    ll n,m,p,c,a[N+10],primes[N+10],p_cnt,phi[59],PHI_=0; bool vis_[N+10],f;
    
    ll ksm(ll a,ll b,ll p){ ll anss=1; while(b>0){
        if(b&1) anss=anss*a; a=a*a,b>>=1; if(a>=p) f=1,a%=p; 
        if(anss>=p) f=1,anss%=p; } return anss; }
    
    ll C(ll a,ll x){ 
        ll tmp=a; if(tmp>phi[x]) tmp=tmp%phi[x]+phi[x];
        for(ll i=x;i>0;i--){ f=0,tmp=ksm(c,tmp,phi[i-1]);
            if(f==1) tmp+=phi[i-1],f=0; } return tmp; }
    
    //-------------线段树---------------//
    
    struct tree{ ll l,r,tag,sum; }seg[N<<2];
    
    void update(ll rt){ //tag:维护每个区间整体(最少)被修改了多少次
        seg[rt].sum=seg[rt<<1].sum+seg[rt<<1|1].sum;
        seg[rt].tag=min(seg[rt<<1].tag,seg[rt<<1|1].tag); }
    
    void build(ll rt,ll l,ll r){
        seg[rt].l=l,seg[rt].r=r; if(l==r){ seg[rt].sum=a[l]; return; }
        ll mid=(l+r)>>1; build(rt<<1,l,mid),build(rt<<1|1,mid+1,r),update(rt); }
    
    void change(ll rt,ll L,ll R){ 
     //每次直接暴力修改,记区间最小标记次数tag,>=PHI_就不改了
        if(seg[rt].tag>=PHI_) return; ll l=seg[rt].l,r=seg[rt].r;
        if(l==r){ seg[rt].sum=C(a[l],++seg[rt].tag)%p; return; }
        ll mid=(l+r)>>1; if(L<=mid) change(rt<<1,L,R);
        if(R>mid) change(rt<<1|1,L,R); update(rt); } //单点修改
    
    ll query(ll rt,ll L,ll R){
        ll l=seg[rt].l,r=seg[rt].r; if(r<L||l>R) return 0;
        if(L<=l&&R>=r) return seg[rt].sum%p;
        return (query(rt<<1,L,R)+query(rt<<1|1,L,R))%p; }
    
    //--------------欧拉部分---------------//
    
    ll Phi(ll x){ ll ans=x; //求欧拉函数
        for(ll i=1;i<=p_cnt&&primes[i]*primes[i]<=x;i++){
            if(!(x%primes[i])) ans=ans/primes[i]*(primes[i]-1);
            while(!(x%primes[i])) x/=primes[i];
        } if(x>1) ans=ans/x*(x-1); return ans; }
    
    void get_prime(){ //欧拉筛素数
        for(ll i=2;i<=max_phi;i++){
          if(!vis_[i]) primes[++p_cnt]=i; for(ll j=1;j<=p_cnt;j++)
           { if(i*primes[j]>max_phi) break; vis_[i*primes[j]]=1; if(i%primes[j]==0) break; }
        } phi[PHI_]=p; while(phi[PHI_]!=1) phi[++PHI_]=Phi(phi[PHI_-1]); phi[++PHI_]=1; }
    
    
    //---------------主程序-----------------//
    
    int main(){
        reads(n),reads(m),reads(p),reads(c);
        for(ll i=1;i<=n;i++) reads(a[i]); get_prime(); build(1,1,n); 
        for(ll i=1,op,l,r;i<=m;i++){
            reads(op),reads(l),reads(r);
            if(op==0) change(1,l,r); //区间替换为c^ai​
            if(op==1) printf("%lld
    ",query(1,l,r)%p);
        }
    }
    p3747 相逢是问候 //欧拉函数 + 线段树 + 极限标记思想(非AC)

    综合练习题系列

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<queue>
    #include<vector>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    //【p2155】莎拉公主的困惑
    
    //列出式子,可以发现:ans = (n!/m!) * m!*∏(i=1~k)​pi/​pi​−1​ (欧拉函数的定义)
    
    void reads(int &x){ //读入优化(正负整数)
        int fx=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
        x*=fx; //正负号
    }
    
    const int N=10000019,M=800019; bool vis_[N];
    
    int jc[N],inv[N],ans[N],primes[M],tot=0,mod;
    
    inline void init(int MAXN){
        jc[0]=jc[1]=1,inv[0]=inv[1]=1,ans[0]=ans[1]=1;
        for(register int i=2;i<=MAXN;i++){ //线性求阶乘&逆元数组
            jc[i]=1LL*jc[i-1]*i%mod,inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
            if(!vis_[i]) primes[++tot]=i; //筛质数
            for(register int j=1;j<=tot&&i*primes[j]<=MAXN;j++)
             { vis_[i*primes[j]]=1; if(i%primes[j]==0) break; } //剪枝优化
        } for(register int i=2;i<=MAXN;i++){ ans[i]=ans[i-1]; //ans:阶乘i!的∏(i=1~k)​pi/​pi​−1​
            if(!vis_[i]) ans[i]=1LL*ans[i]*(i-1)%mod*inv[i]%mod; } } //每次*质因子
    
    int main(){
        int T,n,m; reads(T),reads(mod); init(N-10);
        while(T--) reads(n),reads(m),printf("%lld
    ",1LL*jc[n]*ans[m]%mod);
    } //利用阶乘m!的所有质因子都是1~n线性单调出现的,直接将每个质因子连乘即可
    
    //很神奇的一点是,开了LL真的超级耗时...就一个LL就会TLE两个点,还基本都是900ms+...
    p2155 莎拉公主的困惑 //数学计算 + 阶乘与欧拉函数

                                       ——时间划过风的轨迹,那个少年,还在等你

  • 相关阅读:
    HDU.6681.Rikka with Cake(欧拉公式 树状数组)
    Codeforces.449C.Willem, Chtholly and Seniorious(ODT)
    2017-2018 ACM-ICPC, Asia Daejeon Regional Contest (E,G,H,I,K)
    CF GYM.101987A.Circuits(线段树)
    2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)
    220
    219
    218
    217
    216
  • 原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/10423022.html
Copyright © 2011-2022 走看看