群论的习题
1. Pólya 定理
传送门
∣
A
/
G
∣
=
1
∣
G
∣
∑
g
∈
G
n
c
(
g
)
∣
A
/
G
∣
=
1
∣
G
∣
∑
i
≤
n
∣
n
gcd
(
i
,
n
)
∣
∑
i
≤
n
∣
n
gcd
(
i
,
n
)
∣
=
∑
i
≤
n
∑
g
[
g
=
gcd
(
i
,
n
)
]
n
g
=
∑
g
∣
n
n
g
∑
i
≤
n
[
g
=
gcd
(
i
,
n
)
]
=
∑
g
∣
n
n
g
∑
i
≤
n
[
1
=
gcd
(
i
g
,
n
g
)
]
=
∑
g
∣
n
n
g
ϕ
(
n
g
)
|A / G| = \frac {1}{|G|} \sum_{g \in G} n^{c (g)} \\ |A / G| = \frac {1}{|G|} \sum_{i \leq n} |n ^ {\gcd (i, n)}| \\ \begin{aligned} &\sum_{i \leq n} |n ^ {\gcd (i, n)}| \\ = &\sum_{i \leq n} \sum_{g} [g = \gcd (i, n)]n^g \\ = &\sum_{g \mid n} n^g\sum_{i \leq n} [g = \gcd (i, n)] \\ = &\sum_{g \mid n} n^g\sum_{i \leq n} [1 = \gcd (\frac {i}{g}, \frac{n}{g})] \\ = &\sum_{g \mid n} n^g \phi (\frac{n}{g}) \end{aligned}
∣A/G∣=∣G∣1g∈G∑nc(g)∣A/G∣=∣G∣1i≤n∑∣ngcd(i,n)∣====i≤n∑∣ngcd(i,n)∣i≤n∑g∑[g=gcd(i,n)]ngg∣n∑ngi≤n∑[g=gcd(i,n)]g∣n∑ngi≤n∑[1=gcd(gi,gn)]g∣n∑ngϕ(gn)
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
// #define int long long
#define PII pair <int, int>
#define ULL unsigned long long
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); i++)
#define per(i,j,k) for (int i = (j); i >= (k); i--)
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... Arg) {
read (x), read (Arg...);
}
const int MaxPrint = 1000;
int Poi_For_Print, Tmp_For_Print[MaxPrint + 5];
template <typename T>
void write (T x) {
if (x == 0) {
putchar ('0');
return;
}
bool flag = (x < 0 ? 1 : 0);
x = (x < 0 ? -x : x);
while (x) Tmp_For_Print[++Poi_For_Print] = x % 10, x /= 10;
if (flag) putchar ('-');
while (Poi_For_Print) putchar (Tmp_For_Print[Poi_For_Print--] + '0');
}
template <typename T, typename... Args>
void write (T x, Args... Arg) {
write (x); putchar (' '); write (Arg...);
}
template <typename T, typename... Args>
void print (T x, char ch) {
write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
const LL Mod = 1e9 + 7;
int t, n;
unordered_map <int, int> phi;
int get_phi (int x, int lst) {
int tmp = x;
if (phi.find (x) != phi.end ()) return phi[x];
while (x % lst && lst <= x / lst) lst++;
if (x % lst == 0 && x > 1) {
int fk = 1;
while (x % lst == 0)
x /= lst, fk *= lst;
return phi[tmp] = get_phi (x, lst + 1) * fk / lst * (lst - 1);
}
else {
if (x > 1) return phi[x] = x - 1;
else return 1;
}
}
LL quick_pow (LL x, LL y) {
LL res = 1;
while (y) {
if (y & 1) res = (res * x) % Mod;
x = (x * x) % Mod, y >>= 1;
}
return res;
}
LL inv (LL x) {
return quick_pow (x, Mod - 2);
}
signed main () {
// freopen ("C:\\Users\\BZ\\Desktop\\.vscode\\1.in", "r", stdin);
// freopen ("C:\\Users\\BZ\\Desktop\\.vscode\\1.out", "w", stdout);
// rep (i, 1, 100)
// printf ("phi (%d) = %d\n", i, get_phi (i, 2));
read (t);
while (t--) {
// A / G = \frac {1}{|G|}
read (n);
LL res = 0;
for (int i = 1; i <= n / i; i++)
if (n % i == 0)
res = (res + quick_pow (n, i) * get_phi (n / i, 2) + (i != n / i ? quick_pow (n, n / i) * get_phi (i, 2) : 0)) % Mod;
print (res * inv (n) % Mod, '\n');
}
return 0;
}
2.Cards
传送门
首先塞入一个单位元
∣
A
/
G
∣
=
1
∣
G
∣
∑
g
∈
G
∣
g
A
∣
=
1
∣
G
∣
∑
g
∈
G
∣
g
A
∣
g
A
即
A
关于
g
的轨道大小
枚举
g
,
d
p
求得
g
A
\begin{aligned} |A/G| &= \frac{1}{|G|} \sum_{g \in G} |g^{A}| \\ &= \frac{1}{|G|} \sum_{g \in G} |g^{A}| \\ &g^{A} 即 A 关于 g 的轨道大小 \\ &枚举 g,dp 求得 g^A \end{aligned}
∣A/G∣=∣G∣1g∈G∑∣gA∣=∣G∣1g∈G∑∣gA∣gA即A关于g的轨道大小枚举g,dp求得gA
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
// #define int long long
#define PII pair <int, int>
#define ULL unsigned long long
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); i++)
#define per(i,j,k) for (int i = (j); i >= (k); i--)
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... Arg) {
read (x), read (Arg...);
}
const int MaxPrint = 1000;
int Poi_For_Print, Tmp_For_Print[MaxPrint + 5];
template <typename T>
void write (T x) {
if (x == 0) {
putchar ('0');
return;
}
bool flag = (x < 0 ? 1 : 0);
x = (x < 0 ? -x : x);
while (x) Tmp_For_Print[++Poi_For_Print] = x % 10, x /= 10;
if (flag) putchar ('-');
while (Poi_For_Print) putchar (Tmp_For_Print[Poi_For_Print--] + '0');
}
template <typename T, typename... Args>
void write (T x, Args... Arg) {
write (x); putchar (' '); write (Arg...);
}
template <typename T, typename... Args>
void print (T x, char ch) {
write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
const int Maxn = 100;
const int Maxs = 20;
int a, b, c, m, p, n;
int x[Maxn + 5];
int quick_pow (int x, int y) {
int res = 1;
while (y) {
if (y & 1) res = (res * x) % p;
x = (x * x) % p, y >>= 1;
}
return res;
}
int inv (int x) {
return quick_pow (x, p - 2);
}
struct Union_Find_Set {
int fa[Maxn + 5], _rank[Maxn + 5];
void MakeSet () {
rep (i, 1, Maxn)
fa[i] = i, _rank[i] = 1;
}
int FindSet (int x) {
if (fa[x] != x) fa[x] = FindSet (fa[x]);
return fa[x];
}
void UnionSet (int x, int y) {
int u = FindSet (x), v = FindSet (y);
if (u == v) return;
_rank[v] += _rank[u], fa[u] = v;
}
}Ufs;
bool vis[Maxn + 5];
int num, sz[Maxn + 5];
int dp[Maxs + 5][Maxs + 5][Maxs + 5];
signed main () {
// freopen ("C:\\Users\\BZ\\Desktop\\.vscode\\1.in", "r", stdin);
// freopen ("C:\\Users\\BZ\\Desktop\\.vscode\\1.out", "w", stdout);
read (a, b, c, m, p), n = a + b + c;
int res = 0;
rep (step, 1, m + 1) {
num = 0;
Ufs.MakeSet ();
if (step != m + 1)
rep (i, 1, n)
read (x[i]), vis[i] = 0;
else
rep (i, 1, n)
x[i] = i;
rep (i, 1, n) {
if (vis[i]) continue;
int p = i;
while (!vis[p]) {
vis[p] = 1;
Ufs.UnionSet (p, x[p]);
p = x[p];
}
}
rep (i, 1, n) vis[i] = 0;
rep (i, 1, n) {
if (vis[Ufs.FindSet (i)]) continue;
vis[Ufs.FindSet (i)] = 1;
sz[++num] = Ufs._rank[Ufs.FindSet (i)];
}
rep (i, 0, a)
rep (j, 0, b)
rep (k, 0, c)
dp[i][j][k] = 0;
dp[0][0][0] = 1;
rep (i, 1, num) {
per (j, a, 0)
per (k, b, 0)
per(l, c, 0) {
dp[j][k][l] += (
(j - sz[i] >= 0 ? dp[j - sz[i]][k][l] : 0)
+ (k - sz[i] >= 0 ? dp[j][k - sz[i]][l] : 0)
+ (l - sz[i] >= 0 ? dp[j][k][l - sz[i]] : 0)
) % p;
dp[j][k][l] %= p;
}
// printf("qwq::%d \n",sz[i]);
}
res = (res + dp[a][b][c]) % p;
// printf ("%d::%d %d\n", step, dp[a][b][c], res);
}
// write (dp[a][b][c] % p); putchar ('\n');
write (res * inv (m + 1) % p);
return 0;
}
3.有色图
∣ A / G ∣ = 1 ∣ G ∣ ∑ g ∈ G ∣ A g ∣ |A/G| = \frac{1}{|G|} \sum_{g \in G} |A^{g}| ∣A/G∣=∣G∣1g∈G∑∣Ag∣
a a a 指边组成的群,这个量很大,所以我们考虑枚举点组成的群,对于同个点轮换之间的边,边轮换长度为点轮换长度的一半(向下取整),不同点轮换之间,边轮换长度为两个点轮换长度 ( i & j i \ \& j i &j) 的最小公倍数,则个数为 i ∗ j l c m ( i , j ) = g c d ( i , j ) \frac{i * j}{lcm (i, j)} = gcd (i, j) lcm(i,j)i∗j=gcd(i,j)
对于点轮换长度组成的序列 B B B,边轮换个数为
( ∑ i = 1 n u m ⌊ b i 2 ⌋ ) + ( ∑ i = 1 n u m ∑ j = i + 1 n u m g c d ( b i , b j ) ) (\sum_{i = 1}^{num} \lfloor \frac{b_i}{2} \rfloor) + (\sum_{i = 1}^{num} \sum_{j = i + 1}^{num} gcd (b_i, b_j)) (i=1∑num⌊2bi⌋)+(i=1∑numj=i+1∑numgcd(bi,bj))
考虑每个点属于哪个点群
p r e i = ∑ j = 1 i b j ∑ i ( n − p r e i − 1 p r e i − p r e i − 1 ) = n ! ∏ i b i ! \begin{aligned} &pre_i = \sum_{j = 1}^{i} b_j \\ &\sum_{i} \binom{n - pre_{i - 1}}{pre_i - pre_{i - 1}} \\ = &\frac{n!}{\prod_i b_i!} \end{aligned} =prei=j=1∑ibji∑(prei−prei−1n−prei−1)∏ibi!n!
内部有圆排列,考虑断环成链,然后由于钦定断点,而断点与答案无关,需要消序。
b i ! b i = ( b i − 1 ) ! \frac{b_i!}{b_i} = (b_i-1)! bibi!=(bi−1)!
还要考虑去重,轮换长度相同的点轮换可以交换,对于点轮换长度相同的轮换个数组成的序列 C C C
答案即为
( ∏ i ( b i − 1 ) ! ) 1 ∣ G ∣ n ! ∏ b i ! 1 ∏ c i ! m ( ∑ i = 1 n u m ⌊ b i 2 ⌋ ) + ( ∑ i = 1 n u m ∑ j = i + 1 n u m g c d ( b i , b j ) ) 1 ∏ i b i 1 ∏ i c i ! m ( ∑ i = 1 n u m ⌊ b i 2 ⌋ ) + ( ∑ i = 1 n u m ∑ j = i + 1 n u m g c d ( b i , b j ) ) \begin{aligned} (\prod_i (b_i - 1)!) \frac{1}{|G|} \frac{n!}{\prod b_i!} \frac{1}{\prod c_i!} m^{(\sum_{i = 1}^{num} \lfloor \frac{b_i}{2} \rfloor) + (\sum_{i = 1}^{num} \sum_{j = i + 1}^{num} gcd (b_i, b_j))} \\ \frac{1}{\prod_i b_i} \frac{1}{\prod_i c_i!} m^{(\sum_{i = 1}^{num} \lfloor \frac{b_i}{2} \rfloor) + (\sum_{i = 1}^{num} \sum_{j = i + 1}^{num} gcd (b_i, b_j))} \end{aligned} (i∏(bi−1)!)∣G∣1∏bi!n!∏ci!1m(∑i=1num⌊2bi⌋)+(∑i=1num∑j=i+1numgcd(bi,bj))∏ibi1∏ici!1m(∑i=1num⌊2bi⌋)+(∑i=1num∑j=i+1numgcd(bi,bj))
4.忘了
题意:长度为 n n n 的环,给这个环染色( m m m 种颜色),不允许相邻两个位置染同色,问方案数(通过旋转可变为一样的染色方式算一种)
∣ A / G ∣ = ∑ g ∈ G ∣ A g ∣ |A/G| = \sum_{g \in G} |A^g| ∣A/G∣=g∈G∑∣Ag∣
旋转了 i i i 位,轮换个数为 gcd ( i , n ) \gcd (i, n) gcd(i,n)
可以将每个轮换抽象为一个点,再连起来,容易知道这是等价的,环的长度为 gcd ( i , n ) \gcd (i, n) gcd(i,n)。
令 f ( x ) f(x) f(x) 表示长度为 x x x 的环染色方案数。
∣ A / G ∣ = ∑ g ∈ G ∣ A g ∣ = ∑ i = 1 n f ( gcd ( i , n ) ) = ∑ _ g c d = 1 n f ( _ g c d ) ∑ i = 1 n 1 [ g c d ( i , n ) = _ g c d ] = ∑ _ g B c d = 1 f ( _ g c d ) ϕ ( n _ g c d ) [ _ g c d ∣ n ] \begin{aligned} |A/G| &= \sum_{g \in G} |A^g| \\ &= \sum_{i = 1}^{n} f (\gcd (i, n)) \\ &= \sum_{\_gcd = 1}^n f(\_gcd) \sum_{i = 1}^n 1[gcd (i, n) = \_gcd] \\ &= \sum_{\_gBcd = 1} f (\_gcd) \phi (\frac{n}{\_gcd})[\_gcd | n] \end{aligned} ∣A/G∣=g∈G∑∣Ag∣=i=1∑nf(gcd(i,n))=_gcd=1∑nf(_gcd)i=1∑n1[gcd(i,n)=_gcd]=_gBcd=1∑f(_gcd)ϕ(_gcdn)[_gcd∣n]
{ f ( 0 , 0 , n ) = f ( 0 , 1 , n − 1 ) + f ( 0 , 0 , n − 1 ) f ( 0 , 1 , n ) = f ( 1 , 0 , n − 1 ) ∗ m + f ( 0 , 0 , n − 1 ) ∗ m f ( 1 , 0 , n ) = f ( 1 , 1 , n − 1 ) + f ( 1 , 0 , n − 1 ) f ( 1 , 1 , n ) = f ( 1 , 0 , n − 1 ) ∗ m \begin{cases} f (0, 0, n) = f (0, 1, n - 1) + f (0, 0, n - 1) \\ f (0, 1, n) = f (1, 0, n - 1) * m + f (0, 0, n - 1) * m \\ f (1, 0, n) = f (1, 1, n - 1) + f (1, 0, n - 1) \\ f (1, 1, n) = f (1, 0, n - 1) * m \end{cases} ⎩ ⎨ ⎧f(0,0,n)=f(0,1,n−1)+f(0,0,n−1)f(0,1,n)=f(1,0,n−1)∗m+f(0,0,n−1)∗mf(1,0,n)=f(1,1,n−1)+f(1,0,n−1)f(1,1,n)=f(1,0,n−1)∗m
f ( i ) = ( f ( 0 , 0 , i ) + f ( 1 , 0 , i ) + f ( 0 , 1 , i ) ) n f (i) = \frac{(f (0, 0, i) + f (1, 0, i) + f (0, 1, i))}{n} f(i)=n(f(0,0,i)+f(1,0,i)+f(0,1,i))
矩阵加速求 f f f 即可