【HDU2888】BurningSide定理+因子分解+求逆元+矩陣快速冪

Magic Bracelet
Time Limit: 2000MS   Memory Limit: 131072K
Total Submissions: 4466   Accepted: 1458

Description

Ginny’s birthday is coming soon. Harry Potter is preparing a birthday present for his new girlfriend. The present is a magic bracelet which consists of n magic beads. The are m kinds of different magic beads. Each kind of beads has its unique characteristic. Stringing many beads together a beautiful circular magic bracelet will be made. As Harry Potter’s friend Hermione has pointed out, beads of certain pairs of kinds will interact with each other and explode, Harry Potter must be very careful to make sure that beads of these pairs are not stringed next to each other.

There infinite beads of each kind. How many different bracelets can Harry make if repetitions produced by rotation around the center of the bracelet are neglected? Find the answer taken modulo 9973.

Input

The first line of the input contains the number of test cases.

Each test cases starts with a line containing three integers n (1 ≤ n ≤ 109gcd(n, 9973) = 1), m (1 ≤ m ≤ 10), k (1 ≤ k ≤ m(m − 1) ⁄ 2). The next k lines each contain two integers a and b (1 ≤ ab ≤ m), indicating beads of kind a cannot be stringed to beads of kind b.

Output

Output the answer of each test case on a separate line.

Sample Input

4
3 2 0
3 2 1
1 2
3 2 2
1 1
1 2
3 2 3
1 1
1 2
2 2

Sample Output

4
2
1
0

Source


題意:用m種不同顏色的珠子連成一條長爲n的項鍊,其中,有k對珠子不能相鄰,問總共有多少種(mod 9973)n<10^9,m<=10

題解:組合計數也就burning和polya了,這題用的是Burning Side。

考慮在一種置換f下的穩定核方法,由於只有旋轉對稱,如果是旋轉k個珠子,那麼穩定核的循環節也就是gcd(n,k)=r,枚舉k的話是不現實的,那麼只有枚舉r,即n的所有約數。gcd(n,k)=r,即gcd(n/r,k/r)=1,也就是與n/r互質的數的個數(歐拉函數)就是循環節爲r的置換個數。

對循環節爲r的情況,需要考慮循環節內部珠子的排列,使它們滿足題目要求,還要考慮第一個珠子與最後一個珠子是否滿足要求(最後一個珠子的下一個珠子也是下一輪的第一個珠子),由於珠子種類只有10,可以用鄰接矩陣map[i][j]表示i,j兩種珠子是否能相鄰,如果能,map[i][j]=1,反之,map[i][j]=0,這樣的話,離散數學老師應該說過,如果用0,1矩陣A來表示無向圖的連通情況的話,A^k代表的就是一個點經過k條路後能到達的地方的方法數。

因此,對於循環節爲r的情況,A^r就是任意點經過r條路能到達的地方,與之對應的map[i][i]就是一個珠子經過可行路徑轉了r條路徑又回到自己的種數,其實,就是前面說的滿足題意的排列數,矩陣乘法可以分治加個速,對於n的約數隨便求一下,這道題就出來了。



題意:m種珠子串成由n個珠子構成的環,並且有一些限制,比如第i種與第j種不能相鄰,旋轉後相同算是同一種方案,求方案數。(m<=10,n<=10^9)

如果沒有限制條件,可以很容易用Burnside定理+容斥得到答案。

如果只考慮限制,觀察發現,m很小,n很大。那麼可以通過快速冪得到從第i種到第j種,共有k個珠子的方案數。

再回到Burnside定理,顯然有n種置換,現在問題是如何求每種置換能保持不變的着色方案數:

【POJ】2154 Color已經推出,第i種置換(即旋轉360/n*i度)會有GCD(n,i)種不同的顏色,循環節長度爲GCD(n,i)。

就可以用快速冪得到從第i種回到第i種,共有GCD(n,i)個珠子的方案數。帶回Burnside定理+容斥,問題就解決了。

【POJ】2409 Let it Bead->【POJ】2154 Color->【POJ】2888 Magic Bracelet

#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 32000
#define mod 9973
int n, m;
bool p[N];
std::vector<int> prime;
std::vector<int> factor;
std::vector<int> primefactor;
const int MAXN = 12;
struct Matrix
{
    int mat[MAXN][MAXN];
    void Zero()
    {
        memset(mat, 0, sizeof(mat));
    }
    void Unit()
    {
        memset(mat, 0, sizeof(mat));
        for (int i = 0; i < MAXN; i++)
            mat[i][i] = 1;
    }
} g;
Matrix operator*(Matrix &a, Matrix &b)
{
    Matrix tmp;
    tmp.Zero();
    for (int k = 0; k < MAXN; k++)
    {
        for (int i = 0; i < MAXN; i++)
        {
            if (!a.mat[i][k])
                continue;
            for (int j = 0; j < MAXN; j++)
            {
                tmp.mat[i][j] += a.mat[i][k] * b.mat[k][j] % mod;
            }
        }
    }
    return tmp;
}
Matrix operator ^(Matrix a, int k)
{
    Matrix tmp;
    tmp.Unit();
    for (; k; k >>= 1)
    {
        if (k & 1)
            tmp = tmp * a;
        a = a * a;
    }
    return tmp;
}
void Init()
{
    prime.clear();
    memset(p, 1, sizeof(p));
    for (int i = 2; i < 180; i++)
    {
        if (p[i])
            for (int j = i * i; j < N; j += i)
                p[j] = false;
    }
    for (int i = 2; i < N; i++)
    {
        if (p[i])
            prime.push_back(i);
    }
}
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 Factor(int x)
{
    int i, tmp;
    factor.clear();
    tmp = (int)(sqrt((double)x) + EPS);
    for (i = 1; i <= tmp; i++)
    {
        if (x % i == 0)
        {
            factor.push_back(i);
            if (i == tmp && i * i == x)
                continue;
            factor.push_back(n / i);
        }
    }
}
int NoChange(int x)
{
    int i, ans;
    Matrix tmp;
    tmp = g ^ x;
    for (i = ans = 0; i < m; i++)
    {
        ans += tmp.mat[i][i] % mod;
    }
    return ans;
}
void Prime(int x)
{
    int i, tmp;
    primefactor.clear();
    tmp = (int)(sqrt((double)x) + EPS);
    for (i = 0; prime[i] <= tmp&&i<prime.size(); i++)
    {
        if (x % prime[i] == 0)
        {
            primefactor.push_back(prime[i]);
            while (x % prime[i] == 0)
            {
                x /= prime[i];
            }
        }
    }
    if (x > 1)
        primefactor.push_back(x);
}
int Mul(int x, int &k)
{
    int i, ans;
    ans = 1;
    for (i = k = 0; x; x >>= 1, i++)
    {
        if (x & 1)
        {
            k++;
            ans *= primefactor[i];
        }
    }
    return ans;
}
int Count(int x)
{
    int i, j, t, ans, tmp;
    Prime(x);
    ans = 0;
    t = (int)primefactor.size();
    for (i = 1; i < (1 << t); i++)
    {
        tmp = Mul(i, j);
        if (j & 1)
            ans += x / tmp;
        else
            ans -= x / tmp;
    }
    return (x - ans) % mod;
}
int Burnside()
{
    int i, ans;
    Factor(n);
    for (i = ans = 0; i < (int)factor.size(); i++)
    {
        ans += Count(n / factor[i]) * NoChange(factor[i]) % mod;
    }
    return ans * Inv(n, mod) % mod;
}
int main()
{
#ifdef DeBUGs
    freopen("C:\\Users\\Sky\\Desktop\\1.in", "r", stdin);
#endif
    int T;
    int x, y, k;
    Init();
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        g.Zero();
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < m; j++)
                g.mat[i][j] = 1;
        }
        while (k--)
        {
            scanf("%d%d", &x, &y);
            x--;
            y--;
            g.mat[x][y] = g.mat[y][x] = 0;
        }
        printf("%d\n", Burnside());
    }

    return 0;
}


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