【HDU3923】類似uva10294項鍊種類polya計數+逆元

Invoker

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 122768/62768 K (Java/Others)
Total Submission(s): 1106    Accepted Submission(s): 465


Problem Description
On of Vance's favourite hero is Invoker, Kael. As many people knows Kael can control the elements and combine them to invoke a powerful skill. Vance like Kael very much so he changes the map to make Kael more powerful. 

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.
 

Input
The first line contains a single positive integer T( T <= 500 ), indicates the number of test cases.
For each test case: give you two positive integers n and m. ( 1 <= n, m <= 10000 )
 

Output
For each test case: output the case number as shown and then output the answer mod 1000000007 in a line. Look sample for more information.
 

Sample Input
2 3 4 1 2
 

Sample Output
Case #1: 21 Case #2: 1
Hint
For Case #1: we assume a,b,c are the 3 kinds of elements. Here are the 21 different arrangements to invoke the skills / aaaa / aaab / aaac / aabb / aabc / aacc / abab / / abac / abbb / abbc / abcb / abcc / acac / acbc / / accc / bbbb / bbbc / bbcc / bcbc / bccc / cccc /
 

Source


題解是轉的:
解題思路:

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章