题目链接:
POJ 2888 Magic Bracelet
题意:
有一串
n
个珠子的项链,用
分析:
因为需要考虑旋转,所以很容易想到
Polya计数和Burnside引理。
假设有
k
个循环,用
用
dp[k][i][j]
表示经过
k
个循环从第
初始化: dp[1][i][j]=1−link[i][j]
初始矩阵: A[i][j]=1−link[i][j] 来表示颜色间的连通性
由上面的递推式, Ak[i][j] 代表的是:从第 i 种颜色经过
离散数学里有:如果用0,1矩阵A来表示无向图的连通情况的话,A^k代表的就是一个点经过k条路后能到达的地方的方法数。
假设对于每个循环的步长为
i
,也就是
满足循环个数为
k
的置换的旋转步长
综上对于每个满足
n%k=0
的
k
可以得到的方案数是
枚举每个 k 然后求和最后还要除以
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const ll mod = 9973;
int link[15][15];
struct Matrix{
int row, col;
ll data[15][15];
Matrix operator * (const Matrix& rhs) const { //矩阵相乘条件:col = rhs.row
Matrix res;
res.row = row, res.col = rhs.col;
for(int i = 1; i <= res.row; ++i) {
for(int j = 1; j <= res.col; ++j) {
res.data[i][j] = 0;
for(int k = 1; k <= row; ++k) {
res.data[i][j] += data[i][k] * rhs.data[k][j];
}
res.data[i][j] %= mod;
//要是写成:
//res.data[i][j] = (res.data[i][j] + data[i][k] * rhs.data[k][j] % mod) % mod,
//会TLE,辣鸡
}
}
return res;
}
Matrix operator ^ (const int m) const { //矩阵快速幂
Matrix res, tmp;
res.row = row, res.col = col; //row == col
memset(res.data, 0, sizeof(res.data));
tmp.row = row, tmp.col = col;
memcpy(tmp.data, data, sizeof(data));
for(int i = 1; i <= res.row; ++i) { res.data[i][i] = 1; }
int mm = m;
while(mm) {
if(mm & 1) res = res * tmp;
tmp = tmp * tmp;
mm >>= 1;
}
return res;
}
};
inline ll quick_pow(ll a, ll b, ll p)
{
ll res = 1, tmp = a;
while(b) {
if(b & 1) res = res * tmp % p;
tmp = tmp * tmp % p;
b >>= 1;
}
return res;
}
inline ll phi(int x)
{
ll res = x;
for(int i = 2; i * i <= x; ++i) {
if(x % i == 0) {
res = res / i * (i - 1);
while(x % i == 0) { x /= i; }
}
}
if(x > 1) res = res / x * (x - 1);
return res;
}
inline ll solve(int len, int m)
{
Matrix tmp;
tmp.row = tmp.col = m;
for(int i = 1; i <= m; ++i) {
for (int j = 1; j <= m; ++j) {
tmp.data[i][j] = 1 - link[i][j];
}
}
tmp = tmp ^ len;
ll ans = 0;
for (int i = 1; i <= m; ++i) {
ans = (ans + tmp.data[i][i]) % mod;
}
return ans;
}
int main()
{
int T, n, m, t;
scanf("%d", &T);
while (T--) {
memset(link, 0, sizeof(link));
scanf("%d%d%d", &n, &m, &t);
for(int i = 0; i < t; ++i) {
int former, later;
scanf("%d%d", &former, &later);
link[former][later] = link[later][former] = 1;
}
ll ans = 0;
for(int i = 1; i * i <= n; ++i) {
if(n % i) continue;
ans = (ans + phi(n / i) * solve(i, m)) % mod;
if(n / i == i) continue;
ans = (ans + phi(i) * solve(n / i, m)) % mod;
}
printf("%lld\n", ans * quick_pow(n % mod, mod - 2, mod) % mod);
}
return 0;
}