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