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;
}

posted on 2011-08-15 00:28  lwbaptx  阅读(552)  评论(0编辑  收藏  举报

导航