Invoker
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 122768/62768 K (Java/Others)Total Submission(s): 1106 Accepted Submission(s): 465
In his new map, Kael can control n kind of elements and he can put m elements equal-spacedly on a magic ring and combine them to invoke a new skill. But if a arrangement can change into another by rotate the magic ring or reverse the ring along the axis, they will invoke the same skill. Now give you n and m how many different skill can Kael invoke? As the number maybe too large, just output the answer mod 1000000007.
For each test case: give you two positive integers n and m. ( 1 <= n, m <= 10000 )
Polya計數。題目可轉化爲用c種顏色給n個珠子的項鍊染色,問一共有多少種顏色方案。本題要對結果取模1000000007
1.旋轉。
將環順時針旋轉i格後,循環節個數爲gcd(n,i), 染色方案爲 ∑c^gcd(n,i) 其中 i=1,2,3,4,....n
2.翻轉。
這裏也得考慮兩種情況。
當n爲奇數時,共有n個循環節個數爲(n/2+1)的循環羣,還有的資料上說是環的個數爲(n/2+1) ,注意這是計算機上的表示,n/2整型相除計算機得到的是整數,其實應該寫成(n+1)/2。,染色方案爲 n*c^(n/2+1)
爲什麼n個循環節個數爲(n/2+1)的循環羣呢?我的理解是這樣的,或許不太對。。。
拿正三角形爲例,給它三個頂點染色, 對稱軸是一個頂點與其對邊終點連線所在的直線,這樣的直線有3(n=3,即n個頂點) 條,共有3(n)個循環羣。假設第一個頂點在對稱軸上,那麼第二個頂點經過對稱軸翻轉肯定和第三個頂點重合,那麼 (2,3)是一個循環節,(1)自己是一個循環節,循環節個數爲2,即(n+1/2)。
當n爲偶數時,共有n個循環羣,其中有n/2個的循環節個數爲(n/2 +1), 有n/2個的循環節個數爲(n/2)。
拿正方形爲例,四個頂點從左上角順時針編號1,2,3,4.
當以1,3頂點連線所在直線爲對稱軸時(對角的兩個頂點),這樣對稱軸有2個(n/2),經過翻轉,2,4 重合,1和1重合,3和3重合,那麼循環節的個數爲3(2,4) (1)(3), 即(n/2+1)。 染色方案爲 (n/2)*c^(n/2+1)
當以兩條相對平行的邊的中點連線所在直線爲對稱軸時,比如以線段1,2的中點和3,4的中點連線的所在直線爲對稱軸,這樣的對稱軸有兩個(n/2),經過翻轉,1,2重合,3,4重合,循環節的個數爲2,(1,2)(3,4),即(n/2)。,也就是誰和誰重合,誰就和誰在一個循環節裏。染色方案爲(n/2)*c^(n/2)
最後累加方案得到ans, 再除以置換羣的個數2*n,即 ans/(2*n)%mod即爲最後答案。但這裏要特別注意,ans是在計算過程中不斷取模得到的數,ans,2*n都在模剩餘系中,不能直接參與除法計算,因爲有公式a*b%mod=(a%mod*b%mod)%mod,除法對取餘不滿足結合律,a/b!=((a%mod)/(b%mod))%mod ,在計算 ans/(2*n)%mod時,可以轉化爲 ans*inv(2*n)%mod ,其中 inv(2*n)是2*n關於mod的逆元,保證乘以inv(2*n)和除以 2*n 對於最後的答案取餘mod是一樣。
所以現在的問題是怎樣求一個數關於模P的逆元。
方法1:擴展歐幾里得。 ax=1(mod P), gcd(a,p)=1, 其中x爲a的逆元,就是我們所求,ax=PY+1, ax-Py=1, 所以用擴展歐幾里得可以求出x。
方法2:費馬小定理: 如果模P是素數的話,那麼inv(a)=pow(a,p-2)%p; 等式右邊用快速冪運算可以得出。
代碼自己寫的
#define DeBUG
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <map>
#include <list>
#include <bitset>
using namespace std ;
#define zero {0}
#define INF 0x3f3f3f3f
#define EPS 1e-6
#define TRUE true
#define FALSE false
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x)
{
return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
}
#define N 10005
const long long mod = 1000000007;
long long powmod[N];
LL gcd(LL a, LL b)
{
return b ? gcd(b, a % b) : a;
}
LL Ext_gcd(LL a, LL b, LL &x, LL &y)
{
if (b == 0)
{
x = 1, y = 0;
return a;
}
LL ret = Ext_gcd(b, a % b, y, x);
y -= a / b * x;
return ret;
}
LL Inv(LL a, int m) ///求逆元a相對於m
{
LL d, x, y, t = (LL)m;
d = Ext_gcd(a, t, x, y);
if (d == 1) return (x % t + t) % t;
return -1;
}
void init(long long kind, long long n)
{
powmod[0] = 1;
for (int i = 1; i <= n; i++)
{
powmod[i] = (powmod[i - 1] * kind) % mod;
}
}
long long polya(long long kind, long long n)
{
long long ans = 0;
for (long long i = 1; i <= n; i++)
{
ans = (ans + powmod[gcd(i, n)]) % mod;//旋轉情況
}
if (n & 1)//翻轉情況,注意偶數有兩種對角線
{
ans += (n * powmod[(n) / 2 + 1]) % mod;
}
else
{
ans += (n / 2) * powmod[(n / 2 + 1)] % mod;
ans += (n / 2) * powmod[n / 2] % mod;
}
ans = (ans * Inv(2 * n, mod) + mod) % mod;
return ans;
}
int cnt = 1;
int main()
{
#ifdef DeBUGs
freopen("C:\\Users\\Sky\\Desktop\\1.in", "r", stdin);
#endif
int T;
scanf("%d", &T);
long long kind, n;
while (T--)
{
printf("Case #%d: ", cnt++);
scanf("%I64d%I64d", &kind, &n);
init(kind, n);
printf("%I64d\n", polya(kind, n));
}
return 0;
}