• POJ2154 Color【 polya定理+欧拉函数优化】(三个例题)


    由于这是第一天去实现polya题,所以由易到难,先来个铺垫题(假设读者是看过课件的,不然可能会对有些“显然”的地方会看不懂):

    一:POJ1286 Necklace of Beads :有三种颜色,问可以翻转,可以旋转的染色方案数,n<24。

    1,n比较小,恶意的揣测出题人很有可能出超级多组数据,所以先打表。

    2,考虑旋转:

    for(i=0;i<n;i++)  sum+=pow(n,gcd(n,i));  

    3,考虑翻转:

    if(n&1)  sum+=n*pow(3,n/2+1) ;  
    else {  
          sum+=n/2*pow(3,n/2) ;  
          sum+=n/2*pow(3,n/2+1) ;  
    } 

    4,除以总置换数(2*n=n+n/2+n/2):  

    sum/=(2*n);

    5,终极代码:

    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    using namespace std;
    #define ll long long
    ll ans[30];
    ll gcd(ll a,ll b) {  if(b==0) return a;  return gcd(b,a%b); }
    ll pow(ll a,ll x)
    {
        ll res=1;
        while(x){
            if(x&1) res*=a;
            x>>=1;
            a*=a;
        } return res;
    }
    int main()
    {
        ll n,i,sum;
        for(n=1;n<=23;n++){
            sum=0;
            for(i=0;i<n;i++) sum+=pow(3,gcd(n,i));
            if(n&1)  sum+=n*pow(3,n/2+1) ;  
            else {  
                sum+=n/2*pow(3,n/2) ;  
                sum+=n/2*pow(3,n/2+1) ;  
            }  
            ans[n]=sum/n/2;
        } 
        while(~scanf("%lld",&n)){
            if(n==-1) return 0;
            printf("%lld
    ",ans[n]);
        }
        return 0;
    }
    View Code

    二:HDU3923:Invoker :把3改成m即可。

    1,注意有乘法的话担心超int还是用long long保险。

    2,注意除置换群2*n的时候要逆元。。。比赛的时候就是没有加逆元,导致我牛客第四场排名15。。。不然连续5场前10挺好的。。。

    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int Mod=1000000007;
    #define ll long long
    int gcd(int a,int b) {  if(b==0) return a;  return gcd(b,a%b); }
    int qpow(int a,int x)
    {
        int res=1;a%=Mod;
        while(x){
            if(x&1) res=(ll)res*a%Mod;
            x>>=1;
            a=(ll)a*a%Mod;
        } return res;
    }
    int main()
    {
        int T,n,m,i,Case=0,ans;
        scanf("%d",&T);  while(T--){
            scanf("%d%d",&m,&n);
            ans=0;
            for(i=0;i<n;i++) ans=(ans+qpow(m,gcd(n,i)))%Mod;
            if(n&1)  ans=(ans+n*qpow(m,n/2+1))%Mod;  
            else {  
                ans=(ans+n/2*qpow(m,n/2))%Mod;
                ans=(ans+n/2*qpow(m,n/2+1))%Mod;  
            }  
               ans=(ll)ans%Mod*qpow(2*n,Mod-2)%Mod;
            printf("Case #%d: %d
    ",++Case,ans);
        }   
        return 0;
    }
    View Code

    ----------------------------------------------------分界线----------------------------------------------------

    下面的需要加优化了。

    三:POJ2154 Color: 求可以旋转,不可以翻转的置换,n个珠子,n种颜色,答案mod K,n<1e9。

    那么本题只考虑旋转,则:

    for(i=0;i<n;i++)  ans+=pow(n,gcd(n,i));  
    ans/=n;

    之前莫比乌斯那里,我们常用技巧是合并。  这里考虑gcd相同的合并。(做多了还是有点灵感滴,yeah)。

    得到:

    解决这个公式需要:快速幂+欧拉函数+一些素数的常识。

     1,快速幂不说了,注意先模运算。

     2,欧拉函数,枚举L,即n的素因子,然后根据phi=L*(1-1/p1)*(1-1/p2)...得到欧拉函数。

     3,素数常识,在这里指的是一个数n的素因子最多有一个大于根号n,所以一直除,把根号n前的素数都除完了,留下的一定是大于根号n的一个素数。

    //可以线筛一部分欧拉函数出来,不够的再枚举素数;枚举的话不超过根号个,所以复杂度不会太高。
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=100000;
    int ans, Mod;
    int pri[maxn+10],cnt,vis[maxn+10],phi[maxn+10];
    int qpow(int a,int x)
    {
        int res=1;a%=Mod;//这里a一定要除一下,不然会超int,猪啊。 
        while(x){
            if(x&1) res=res*a%Mod;
            x>>=1; a=a*a%Mod;
        } return res%Mod;
    }
    void prime()
    {
        phi[1]=1;
        for(int i=2;i<=maxn;i++){
            if(!vis[i]) pri[++cnt]=i,phi[i]=i-1;
            for(int j=1;j<=cnt&&pri[j]*i<=maxn;j++) {
                vis[pri[j]*i]=1;
                if(i%pri[j]==0) {
                    phi[i*pri[j]]=phi[i]*pri[j];
                    break;
                }
                else phi[i*pri[j]]=phi[i]*(pri[j]-1);
            }
        }
    }
    int Phi(int x)
    {
        if(x<=maxn) return phi[x]%Mod; 
        int res=x;
        for(int i=1;pri[i]*pri[i]<=x;i++)
          if(x%pri[i]==0){
              res=(res-res/pri[i]);
              while(x%pri[i]==0) x/=pri[i];
         }
        if(x!=1) res=(res-res/x);
        return res%Mod;
    }
    int main()
    {
        int n,T; prime();
        scanf("%d",&T);
        while(T--){
            ans=0; scanf("%d%d",&n,&Mod); 
            for(int i=1;i*i<=n;i++){
                if(n%i!=0) continue;
                ans=(ans+qpow(n,i-1)*Phi(n/i))%Mod;
                if(i*i!=n) ans=(ans+qpow(n,n/i-1)*Phi(i))%Mod;
            }
            printf("%d
    ",ans);
        } return 0;
    }

    --------------------【优化】-----------

    --------------------【here】-------------

    这些基本的都可以用欧拉函数来优化,比如上面的第二个例子。

            上面的代码是500+ms。 

            优化后0ms。

    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=10000;
    const int Mod=1e9+7; 
    #define ll long long
    ll ans;
    ll pri[maxn+10],cnt,vis[maxn+10],phi[maxn+10];
    ll qpow(ll a,ll x)
    {
        ll res=1;a%=Mod;//这里a一定要除一下,不然会超int,猪啊。 
        while(x){
            if(x&1) res=res*a%Mod;
            x>>=1; a=a*a%Mod;
        } return res%Mod;
    }
    void prime()
    {
        phi[1]=1;
        for(int i=2;i<=maxn;i++){
            if(!vis[i]) pri[++cnt]=i,phi[i]=i-1;
            for(int j=1;j<=cnt&&pri[j]*i<=maxn;j++) {
                vis[pri[j]*i]=1;
                if(i%pri[j]==0) {
                    phi[i*pri[j]]=phi[i]*pri[j];
                    break;
                }
                else phi[i*pri[j]]=phi[i]*(pri[j]-1);
            }
        }
    }
    int main()
    {
        ll n,m,T,Case=0; prime();
        scanf("%lld",&T);
        while(T--){
            ans=0; scanf("%lld%lld",&m,&n); 
            for(ll i=1;i*i<=n;i++){
                if(n%i!=0) continue;
                ans=(ans+qpow(m,i)*phi[n/i])%Mod;
                if(i*i!=n) ans=(ans+qpow(m,n/i)*phi[i])%Mod;
            }
            if(n&1)  ans=(ans+n*qpow(m,n/2+1))%Mod;  
            else {  
                ans=(ans+n/2*qpow(m,n/2))%Mod;  
                ans=(ans+n/2*qpow(m,n/2+1))%Mod;  
            } 
            ans=ans%Mod*qpow(2*n,Mod-2)%Mod;
            printf("Case #%lld: %lld
    ",++Case,ans);
        } return 0;
    }


    其他类似的题目还有,HDOJ:2084、2647、1812、3411、2865、2481。POJ:1286、2409、2154、2888。

  • 相关阅读:
    设计模式之四(抽象工厂模式第二回合)
    C# try catch finally
    设计模式之三(工厂方法模式)
    C# XML与Json之间的相互转换
    Google免费的SVN服务器管理VS2010代码
    设计模式之四(抽象工厂模式第一回合)
    MVC项目开发中那些用到的知识点(Jquery ajax提交Json后台处理)
    设计模式之四(抽象工厂模式第三回合)
    设计模式之二(简单工厂模式)
    Jquery的$命名冲突
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8418430.html
Copyright © 2020-2023  润新知