hdu 3923 Invoker

http://acm.hdu.edu.cn/showproblem.php?pid=3923

题意大概就是有条n长度的项链,m种不同的颜色,问可以组成多少种不同的项链(翻转与旋转后相同的都算是同一条项链)

解法:polya+乘法逆元

题目显然就是很裸的polya,不过有些地方要注意下的,

先简单介绍下polya。

这里有个比较详细的讲解 http://hi.baidu.com/%C1%E9%C1%FAzys/blog/item/6d5c0d3e4fc887d19f3d6212.html

fun(a,b) 表示乘幂 a^b

euler(a) 表示a的欧拉函数

n 长度 c 颜色数

polya的模板:

int ans = 0;       
for (int i = 1; i <= n; i++)

{
   if (n % i == 0)            
      ans += fun(c, i) * euler(n / i);      
}      
if (n & 1)
     ans += n * fun(c, n / 2 + 1);      
else
     ans += n / 2 * (fun(c, n / 2) + fun(c, n / 2 + 1));     
cout << ans / (2 * n) << endl;

当n比较大的时候,这样直接枚举n就有点太暴力了,可以把n整数分解,直接去枚举他的因子即可
void dfs(int step, int now, int n)
{   
    if (step >= cnt + 1)
    {       
       ans = (ans + fun(n % mod, now - 1) * euler(n / now) % mod) % mod;
       return;   
    } 
    for (int i = 0, t = 1; i <= c[step]; t *= p[step], i++)      
       dfs(step + 1, now * t, n);
}

好了,polya的理解现在到了这里,不过还有个问题,就是ans超出64位的问题。

所以ans一直要mod一个数

到最后输出的结果是 ans/(2*n),注意。这时候不能直接相除,因为ans是模p之后的结果。

到这里就要用到乘法逆元来处理一下了。

简单说下乘法逆元,具体可以百度百科

a*x=1(mod b)  (gcd(a,b)=1,否则无解)

x称为a关于b的乘法逆元.

关于求乘法逆元可以用扩展的欧几里得算法来求

a*x=1(mod b)

=> a*x=b*k+1.

=> a*x-b*k=1.

这样就可用扩展欧几里得算出x出来了。

上面说到求 ans/(2*n) % p

ans/(2*n)%p = ((ans*x)/(2*n*x))%p; (假设x是2*n关于p的乘法逆元,p=1000000007,显然gcd(2*n,p)=1)

= ans*x%p;

求解完毕。

#include <iostream>

#include <cstdio>

#include <string.h>

#include <algorithm>

#include <cmath>

#include <vector>

using namespace std;

const int mod=1000000007;



__int64 n,c;

__int64 fun(__int64 a,__int64 b)

{

    __int64 t=1,y=a;

    for(int i=1;i<=b;i*=2)

    {

        if(b&i)

          t=t*y%mod;

        y=y*y%mod;

    }

    return t;

}

__int64 euler(__int64 a)

{

    __int64 ans=a;

    for(int i=2;i<=a;i++)

    {

        if(a%i==0)

            ans-=ans/i;

        while(a%i==0)

            a/=i;

    }

    if(a>1)

        ans-=ans/a;

    return ans;

}

__int64 Extend_euclid(__int64 a,__int64 b,__int64 &x,__int64 &y)

{

    __int64 d=0,t=0;

    if(b==0)

    {

        x=1;

        y=0;

        return a;

    }

    else

    {

        d=Extend_euclid(b,a%b,x,y);

        t=x;

        x=y;

        y=t-a/b*y;

    }

    return d;

}

__int64 Bignum_Div(__int64 a,__int64 b)

{

    __int64 x=0,y=0;

    Extend_euclid(b,mod,x,y);

    __int64 ans= a*x%mod;

    while(ans<0)

        ans+=mod;

    return ans;

}

int main()

{



    __int64 ans=0,t=1,T=0;

    scanf("%I64d",&T);

    while(T--)

    {

        scanf("%I64d %I64d",&c,&n);

        ans=0;

        for(int i=1;i<=n;i++)

        {

            if(n%i==0)

            {

                ans+=fun(c,i)*euler(n/i);

                ans%=mod;

            }

        }

        if(n&1)

        {

            ans+=n*fun(c,n/2+1);

            ans%=mod;

        }

        else

        {

            ans+=n/2*( fun(c,n/2)+fun(c,n/2+1));

            ans%=mod;

        }

        //printf("%I64d\n",ans);

        ans=Bignum_Div(ans,2*n);

        printf("Case #%I64d: %I64d\n",t++,ans);

    }

    return 0;

}

你可能感兴趣的:(HDU)