Magic Bracelet(POJ 2888,Burnside 引理 + 矩阵快速幂优化 dp)

一、题目链接

Magic Bracelet

二、题目大意

给定 m m m 种不同颜色的魔法珠子,每种颜色的珠子的个数都足够多。

现在要从中挑选 n n n 个珠子,串成一个环形魔法手链。

魔法珠子之间存在 k k k 对排斥关系,互相排斥的两种颜色的珠子不能相邻,否则会发生爆炸。(同一种颜色的珠子之间也可能存在排斥

请问一共可以制作出多少种不同的手链。

注意,如果两个手链经旋转后能够完全重合在一起,对应位置的珠子颜色完全相同,则视为同一种手链。

答案对 99739973 99739973 99739973 取模。

1 ≤ T ≤ 10 1 ≤ n ≤ 1 0 9 g c d ( n , 9973 ) = 1 1 ≤ m ≤ 10 0 ≤ k ≤ m ( m + 1 ) 2 1 ≤ a , b ≤ m 1 \leq T \leq 10 \quad 1 \leq n \leq 10^9 \quad gcd(n,9973)=1 \quad 1 \leq m \leq 10 \quad 0 \leq k \leq \frac{m(m+1)}{2} \quad 1 \leq a,b \leq m 1T101n109gcd(n,9973)=11m100k2m(m+1)1a,bm.

三、分析

由于循环之间存在限制关系,故无法使用 P o ˊ l y a P\acute olya Poˊlya 定理,而只能使用 B u r n s i d e Burnside Burnside 引理.

旋转操作共有 n n n 种置换,考虑一个置换 P i P_i Pi:顺时针旋转 i i i 个珠子( 1 ≤ i ≤ n 1 \leq i \leq n 1in).

通过二元一次不定方程解得循环长度为 n g c d ( n , i ) \frac{n}{gcd(n,i)} gcd(n,i)n,于是循环个数为 g c d ( n , i ) gcd(n,i) gcd(n,i).

因此置换 P i P_i Pi 的不动点个数等于长度为 g c d ( n , i ) gcd(n,i) gcd(n,i) 的环的合法方案数,记为 F ( g c d ( n , i ) ) F(gcd(n,i)) F(gcd(n,i)).

根据 B u r n s i d e Burnside Burnside 引理可得答案为 A N S = 1 n ∑ i = 1 n F ( g c d ( n , i ) ) \begin{aligned} ANS = \frac{1}{n}\sum_{i=1}^n{F(gcd(n,i))} \end{aligned} ANS=n1i=1nF(gcd(n,i)).

由于 n n n 最大 1 0 9 10^9 109,直接枚举 i i i 是无法接受的. 而 g c d ( n , i ) gcd(n,i) gcd(n,i) 最多只有 1600 1600 1600 种,所以我们去枚举 g c d ( n , i ) gcd(n,i) gcd(n,i).

于是 A N S = 1 n ∑ d ∣ n φ ( n d ) F ( d ) \begin{aligned} ANS = \frac{1}{n}\sum_{d|n}\varphi(\frac{n}{d})F(d) \end{aligned} ANS=n1dnφ(dn)F(d),其中 φ ( n d ) \varphi(\frac{n}{d}) φ(dn) 可以先预处理出 1 0 5 10^5 105 以内的质数再 O ( n / d l n ( n / d ) ) O(\frac{\sqrt{n/d}}{ln(n/d)}) O(ln(n/d)n/d ) 求出.

考虑如何计算 F ( d ) F(d) F(d),记 c [ i ] [ j ] c[i][j] c[i][j] 表示颜色 i i i 和颜色 j j j 的珠子能否相邻, f x [ i ] [ j ] f_x[i][j] fx[i][j] 为第一颗珠子的颜色为 x x x 且 确定了前 i i i 颗珠子的颜色 且 第 i i i 颗珠子颜色为 j j j 的方案数.

则有递推式 f x [ i ] [ j ] = ∑ c [ j ] [ k ] f x [ i − 1 ] [ k ] ( 2 ≤ i ≤ n 1 ≤ j , k ≤ m ) \begin{aligned} f_x[i][j] = \sum_{c[j][k]} f_x[i - 1][k] \quad (2 \leq i \leq n \quad 1 \leq j,k \leq m) \end{aligned} fx[i][j]=c[j][k]fx[i1][k](2in1j,km).

于是 F ( d ) = ∑ x ∑ c [ x ] [ j ] f x [ d ] [ j ] \begin{aligned} F(d) = \sum_{x}\sum_{c[x][j]} f_x[d][j] \end{aligned} F(d)=xc[x][j]fx[d][j]

直接 d p dp dp 的时间复杂度是 O ( n m 3 ) O(nm^3) O(nm3) 的,不难发现可以使用矩阵快速幂加速 d p dp dp 递推.

[ f 1 [ i ] [ 1 ] f 1 [ i ] [ 2 ] ⋯ f 1 [ i ] [ m ] f 2 [ i ] [ 1 ] f 2 [ i ] [ 2 ] ⋯ f 2 [ i ] [ m ] ⋮ ⋮ ⋮ f m [ i ] [ 1 ] f m [ i ] [ 2 ] ⋯ f m [ i ] [ m ] ] = [ f 1 [ i − 1 ] [ 1 ] f 1 [ i − 1 ] [ 2 ] ⋯ f 1 [ i − 1 ] [ m ] f 2 [ i − 1 ] [ 1 ] f 2 [ i − 1 ] [ 2 ] ⋯ f 2 [ i − 1 ] [ m ] ⋮ ⋮ ⋮ f m [ i − 1 ] [ 1 ] f m [ i − 1 ] [ 2 ] ⋯ f m [ i − 1 ] [ m ] ] [ c [ 1 ] [ 1 ] c [ 1 ] [ 2 ] ⋯ c [ 1 ] [ m ] c [ 2 ] [ 1 ] c [ 2 ] [ 2 ] ⋯ c [ 2 ] [ m ] ⋮ ⋮ ⋮ c [ m ] [ 1 ] c [ m ] [ 2 ] ⋯ c [ m ] [ m ] ] \left[ \begin{matrix} f_1[i][1] & f_1[i][2] & \cdots & f_1[i][m] \\ f_2[i][1] & f_2[i][2] & \cdots & f_2[i][m] \\ \vdots & \vdots & & \vdots \\ f_m[i][1] & f_m[i][2] & \cdots & f_m[i][m] \end{matrix} \right] = \left[ \begin{matrix} f_1[i-1][1] & f_1[i-1][2] & \cdots & f_1[i-1][m] \\ f_2[i-1][1] & f_2[i-1][2] & \cdots & f_2[i-1][m] \\ \vdots & \vdots & & \vdots \\ f_m[i-1][1] & f_m[i-1][2] & \cdots & f_m[i-1][m] \end{matrix} \right] \left[ \begin{matrix} c[1][1] & c[1][2] & \cdots & c[1][m] \\ c[2][1] & c[2][2] & \cdots & c[2][m] \\ \vdots & \vdots & & \vdots \\ c[m][1] & c[m][2] & \cdots & c[m][m] \end{matrix} \right] f1[i][1]f2[i][1]fm[i][1]f1[i][2]f2[i][2]fm[i][2]f1[i][m]f2[i][m]fm[i][m]=f1[i1][1]f2[i1][1]fm[i1][1]f1[i1][2]f2[i1][2]fm[i1][2]f1[i1][m]f2[i1][m]fm[i1][m]c[1][1]c[2][1]c[m][1]c[1][2]c[2][2]c[m][2]c[1][m]c[2][m]c[m][m]

于是

[ f 1 [ d ] [ 1 ] f 1 [ d ] [ 2 ] ⋯ f 1 [ d ] [ m ] f 2 [ d ] [ 1 ] f 2 [ d ] [ 2 ] ⋯ f 2 [ d ] [ m ] ⋮ ⋮ ⋮ f m [ d ] [ 1 ] f m [ d ] [ 2 ] ⋯ f m [ d ] [ m ] ] = [ c [ 1 ] [ 1 ] c [ 1 ] [ 2 ] ⋯ c [ 1 ] [ m ] c [ 2 ] [ 1 ] c [ 2 ] [ 2 ] ⋯ c [ 2 ] [ m ] ⋮ ⋮ ⋮ c [ m ] [ 1 ] c [ m ] [ 2 ] ⋯ c [ m ] [ m ] ] d − 1 \left[ \begin{matrix} f_1[d][1] & f_1[d][2] & \cdots & f_1[d][m] \\ f_2[d][1] & f_2[d][2] & \cdots & f_2[d][m] \\ \vdots & \vdots & & \vdots \\ f_m[d][1] & f_m[d][2] & \cdots & f_m[d][m] \end{matrix} \right] = \left[ \begin{matrix} c[1][1] & c[1][2] & \cdots & c[1][m] \\ c[2][1] & c[2][2] & \cdots & c[2][m] \\ \vdots & \vdots & & \vdots \\ c[m][1] & c[m][2] & \cdots & c[m][m] \end{matrix} \right]^{d-1} f1[d][1]f2[d][1]fm[d][1]f1[d][2]f2[d][2]fm[d][2]f1[d][m]f2[d][m]fm[d][m]=c[1][1]c[2][1]c[m][1]c[1][2]c[2][2]c[m][2]c[1][m]c[2][m]c[m][m]d1

四、代码实现

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int M = (int)1e5;
const int N = (int)1e5;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int mod = (int)9973;

int n, m, k;
bool c[10][10];

struct Matrix
{
    int D[10][10];

    Matrix operator*(const Matrix& B)const
    {
        Matrix C;
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < m; ++j)
            {
                C.D[i][j] = 0;
                for(int k = 0; k < m; ++k)
                {
                    C.D[i][j] = (C.D[i][j] + D[i][k] * B.D[k][j]) % mod;
                }
            }
        }
        return C;
    }
};

Matrix quick(Matrix A, int b)
{
    Matrix S;
    if(b == 0)
    {
        for(int i = 0; i < m; ++i)
            for(int j = 0; j < m; ++j) S.D[i][j] = (i == j);
    }
    else
    {
        S = A; --b;
        while(b)
        {
            if(b & 1) S = S * A;
            A = A * A;
            b >>= 1;
        }
    }
    return S;
}

bool is_prime[M + 5];
int prime[M + 5], cnt;

void init()
{
    memset(is_prime, 1, sizeof(is_prime));
    is_prime[0] = is_prime[1] = 0;
    for(int i = 2; i <= M; ++i)
    {
        if(is_prime[i]) prime[++cnt] = i;
        for(int j = 1; j <= cnt && i * prime[j] <= M; ++j)
        {
            is_prime[i * prime[j]] = 0;
            if(i % prime[j] == 0) break;
        }
    }
}

int F(int d)
{
    int ans = 0;
    Matrix A;

    for(int i = 0; i < m; ++i)
    {
        for(int j = 0; j < m; ++j)
        {
            A.D[i][j] = c[i][j];
        }
    }
    A = quick(A, d - 1);
    for(int i = 0; i < m; ++i)
    {
        for(int j = 0; j < m; ++j)
        {
            if(c[i][j]) ans = (ans + A.D[i][j]) % mod;
        }
    }
    return ans;
}

int phi(int n)
{
    int ans = n;
    for(int i = 1; i <= cnt && 1ll * prime[i] * prime[i] <= n; ++i)
    {
        if(n % prime[i] == 0)
        {
            ans = ans / prime[i] * (prime[i] - 1);
            while(n % prime[i] == 0) n /= prime[i];
        }
    }
    if(n > 1) ans = ans / n * (n - 1);
    return ans;
}

int quick(int a, int b, int p = mod)
{
    a %= p;
    int s = 1;
    while(b)
    {
        if(b & 1) s = s * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return s % p;
}

int inv(int n, int p = mod)
{
    return quick(n, p - 2, p);
}

void work()
{
    scanf("%d %d %d", &n, &m, &k);
    for(int i = 0; i < m; ++i) for(int j = 0; j < m; ++j) c[i][j] = 1;
    for(int i = 1, a, b; i <= k; ++i)
    {
        scanf("%d %d", &a, &b);
        --a, --b;
        c[a][b] = c[b][a] = 0;
    }

    int ans = 0;
    for(int d = 1; d * d <= n; ++d)
    {
        if(n % d) continue;
        ans = (ans + phi(n / d) % mod * F(d)) % mod;
        if(d != n / d) ans = (ans + phi(d) % mod * F(n / d)) % mod;
    }
    ans = ans * inv(n) % mod;
    printf("%d\n", ans);
}

int main()
{
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
    init();
    int T; scanf("%d", &T);
    while(T--) work();
//    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值