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;
}
浙公网安备 33010602011771号