浅谈群论
群
一些基础
子群
若\(H\)是\(G\)的子集且\(<H,op>\)为群,则\(<H,op>\)为\(<G,op>\)的子群
则\(H\)既满足封闭性且求逆封闭,\(\forall a,b\in H,ab\in H,a^{-1}\in H\)
等价于\(\forall a,b\in H,ab^{-1}\in H\)
一些特殊特殊的子群:
生成子群:\(a\in G\),则\(<\{a^i,i\in Z\},op>\)称为生成子群
正规化子:\(a\in G\),则\(<\{x|ax=xa,x\in G>\)称为正规化子,记为\(N(a)\)
共轭子群:\(a\in G,H\)为\(G\)的子群,则\(xHx^{-1}\)称为\(H\)的共轭子群
等价类
等价关系:满足自反性\(a=a\),对称性\(a=b\Leftrightarrow b=a\),传递性\(a=b,b=c\Leftrightarrow a=c\)(\(=\)代表的是等价关系)
等价类:\(x\)的等价类\([x]_R=\{y|<x,y>\in R\}\),\(R\)是满足某种等价关系两个元素所有集合
可以认为是把等价关系看作边,\([x]_R\)是\(x\)所在联通块的大小
商集:\([A/R]\)指在以\(R\)为等价关系时等价类的集合
陪集
陪集分为右陪集与左陪集,两个没区别
对于\(a\in G\),\(H\)是\(G\)的子群,称\(Ha=\{ha|h\in H\}\)为\(H\)的右陪集
如果\(H\)为有限集,则\(|Ha|=|H|\)(不会证)
Lagrange定理
设\(H\)为\(G\)的子群,则\(|G|\)为\(|H|\)的倍数
考虑用陪集分解群
首先有个结论,\(\forall a,b\in G,H\)为\(G\)的子群,\(a\in Hb\Leftrightarrow Ha=Hb\Leftrightarrow ab^{-1}\in H\)
若已知\(a\in Hb\),则\(a=h_1b,h_1\in H\),\(\forall h_2\in H\),\(h_2a=h_2h_1b\),且\(h_2h_1\in H\)
则\(Ha\subseteq Hb\),反过来同理的\(Hb\subseteq Ha\),即\(Ha=Hb\)
若已知\(Ha=Hb\),则\(\exist h_1,h_2\in H,h_1a=h_2b\),\(ab^{-1}=h_1^{-1}h_2\in H\)
若已知\(ab^{-1}\in H\),则\(ab^{-1}=h\in H\),则\(a=hb\in Hb\)
如果将\(Ha=Hb\)视为一种等价关系,\(H\)一定单独是一个等价类
若\(a\notin H\),则\(Ha\not=He\),即\(a\)一定不与\(e\)在同一等价类
又\(|Ha|=|H|\),所以所有等价类大小相同
即\(\dfrac{|G|}{|H|}=|[G/R]|,R=\{<a,b>,Ha=Hb\}\)
由此还可以得到共轭类分解
共轭关系也是一种等价关系,将\(a\in G\),所有与\(a\)共轭的\(b\)形成的集合称为共轭类
\(a\)所在的共轭类大小为\(\dfrac{|G|}{|N(a)|}\)
令\(x,y\in G,xax^{-1}=yay^{-1}\)
\(xa=yay^{-1}x\Rightarrow y^{-1}xa=ay^{-1}x\Rightarrow y^{-1}x\in N(a)\Rightarrow xN(a)=yN(a)\)
如果沿用陪集分解的思路,因为\(xN(a)=yN(a)\)则\(x,y\)属于同一个等价类
用\(N(a)\)陪集分解,对于其中的一个等价类中所有的元素\(x\),\(xax^{-1}\)确定\(a\)的一个共轭
则共轭类的大小即为\(N(a)\)陪集分解后的等价类个数
置换群相关定理
置换群
置换群即为一个\(n\)元排列\(P\)组成的集合,定义运算\(PG=(G_{P_i})\)
可证满足封闭性与求逆封闭
如果将\(i\)和\(P_i\)连有向边,则图为若干个不相交的环(\(n\)条边\(n\)个点)
当然,有时置换群不一定是一个排列的集合,但一定是置换的集合
轨道-稳定子群定理
定义一个集合\(A\),\(G\)为一个作用于\(A\)的置换群,\(a\in A\)
定义\(G^a=\{g|g(a)=a,g\in G \}\),称为稳定子群
\(G(a)=\{g(a),g\in G \}\),称为轨道
\(|G|=|G(a)|\times|G^a|\),证明如下
设\(x,y\in G\),且\(x(a)=y(a)\),则\(\Leftrightarrow a=x^{-1}(y(a))\Leftrightarrow x^{-1}y\in G^a\Leftrightarrow xG^a=yG^a\)
将\(G\)以\(G^a\)陪集分解,则当\(x(a)=y(a)\)时\(x,y\)属于同一等价类
考虑等价类的个数即为有多少个不同的\(x(a)\)即为\(|G(a)|\)
Burnside 引理
\([A/G]=\dfrac{1}{|G|}\sum\limits_{g\in G}[A^g]\),\(A^g\)的定义与\(G^a\)类似,就是\(A^g=\{a|g(a)=a,a\in A \}\)
\(|G^a|=\dfrac{|G|}{|G(a)|}\),两边同时求和
\(\sum\limits_{a\in A}|G^a|=\sum\limits_{a\in A}\dfrac{|G|}{|G(a)|}=|G|\sum\limits_{a\in A}\dfrac{1}{|G(a)|}\)
观察\(\sum\limits_{a\in A}\dfrac{1}{|G(a)|}\),本质为轨道个数(每一个\(a\)所在的等价类大小分之\(1\)求和就是等价类的个数)=\([A/G]\)
\(\sum\limits_{a\in A}|G^a|=\sum\limits_{g\in G}[A^g]=|G|\times|[A/G]|\)
即\([A/G]=\dfrac{1}{|G|}\sum\limits_{g\in G}[A^g]\)
在这里我们给问题赋予一个实际意义
考虑\(A\)表示问题的所有方案,\(G\)为问题视为重复方案的置换
\([A/G]\)即为将\(G\)看作一个等价关系的集合后划分出的等价类集合
\(G^a\)即为满足对\(a\)置换作用后依旧不变的置换,\(A^g\)差不多
\(G(a)\)为与\(a\)一起视为一种方案的方案集合,也可一看作是\(a\)所在的等价类
再具体一点的例子就是环的着色问题
Pólya 定理
具体到染色问题,假设有\(m\)种颜色
则\(A^g=m^{c(g)}\),\(c(g)\)为\(g\)的不相交循环个数
【模板】Pólya 定理
给定一个 \(n\) 个点,\(n\) 条边的环,有 \(n\) 种颜色,给每个顶点染色,问有多少种本质不同的染色方案,答案对 \(10^9+7\) 取模
注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方案相同。
很明显\(G\)为一个轮换了\(i\)次的置换群
问题在于计算\(c(g)\),考虑\(g\)是轮换了\(i\)次的的置换,当前位置为\(p\)
\(p->(p+i)mod\ n->(p+2i)mod\ n.....p'mod\ n=p\)
即\(p+(n/c(g))i=p+kn\),即\(c(g)=\dfrac{i}{k}\),则\(c(g)\)既为\(n\)的因数也为\(i\)的因数且最大
则\(c(g)=gcd(i,n)\)
\([A/G]=\dfrac{1}{|G|}\sum\limits_{g\in G} n^{c(g)}=\dfrac{1}{n}\sum\limits_{i=1}^nn^{gcd(i,n)}\)
令\(f(x)=n^x\)
\([A/G]=\dfrac{1}{n}\sum\limits_{i=1}^nf(gcd(i,n))=\dfrac{1}{n}\sum\limits_{d|n}f(d)\sum\limits_{i=1}[gcd(i,n)=d]=\dfrac{1}{n}\sum\limits_{d|n}f(d)\phi(\dfrac{n}{d})\)
这里用\(dfs\)凑因子可以做到\(\Theta(\sqrt n)\)
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
int t;
int n;
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
vector<pair<int,int> >Rec;
int Phi[105][105];
int Pri[105][105];
int Used[105];
int Res=0;
void dfs(int x)
{
if(x==Rec.size())
{
int d=1;
int phi=1;
for(int i=0;i<Rec.size();i++)
{
d=(d*Pri[i][Used[i]]);
phi=(phi*Phi[i][Used[i]]);
}
Res=((long long)Res+((long long)phi*Pow(n,(n/d)-1,MOD))%MOD)%MOD;
return;
}
int Lim=Rec[x].second;
for(int i=0;i<=Lim;i++)
{
Used[x]=i;
dfs(x+1);
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
Rec.clear();
scanf("%d",&n);
Res=0;
int now=n;
for(int d=2;d*d<=now;d++)
{
if(now%d==0)
{
int Tot=0;
while(now%d==0)
{
now/=d;
Tot++;
}
Rec.push_back(make_pair(d,Tot));
}
}
if(now>1)
{
Rec.push_back(make_pair(now,1));
}
for(int i=0;i<Rec.size();i++)
{
int Lim=Rec[i].second;
int p=Rec[i].first;
Phi[i][0]=1;
Pri[i][0]=1;
for(int j=1;j<=Lim;j++)
{
Pri[i][j]=Pri[i][j-1]*p;
Phi[i][j]=Pri[i][j]-Pri[i][j-1];
}
}
dfs(0);
printf("%d\n",Res);
}
}
Magic Bracelet
金妮的生日快到了。哈利波特正在为他的新女友准备生日礼物。礼物是一个由\(n\)颗魔法珠组成的魔法手镯。有\(m\)种不同的魔珠。每种珠子都有其独特的特征。将许多珠子串在一起,将制作一个漂亮的圆形魔法手镯。正如哈利波特的朋友赫敏所指出的那样,某些种类的珠子会相互作用并爆炸,哈利波特必须非常小心地确保这些对的珠子不会并排串在一起,有无数种珠子。如果忽略围绕手镯中心旋转产生的重复,哈利能制作多少种不同的手镯?找到取模 \(9973\) 的答案。
同样定义\(G\)为轮换\(i\)次的置换群,但由于不能随便染色,所以不能用\(Pólya\)定理
\([A/G]=\dfrac{1}{|G|}\sum\limits_{g\in G}|A^g|\)
瓶颈在于计算\(|A^g|\)
我们将\(g\)拆分成不同的循环,这些循环的内部的点颜色是相同的且每个循环大小相同,问题是不同循环之间的关系
如果我们把一个循环看成一个点,再将和他有关系的连边,最后连出还是一个环
我们可以考虑只在这个环上计算答案
设\(f(x)\)为长度为\(x\)的环时的答案
\([A/G]=\dfrac{1}{n}\sum\limits_{g\in G}|A^g|=\dfrac{1}{n}\sum\limits_{d|n}f(d)\phi(\dfrac{n}{d})\)
现在问题在与如何计算\(f(x)\)
构造一个邻接矩阵\(T\),矛盾为\(0\),否则为\(1\),则\(T^x\)时的对角线之和即为\(f(x)\)
#include<cstdio>
#include<vector>
#include<utility>
#include<cstring>
using namespace std;
const int MOD=9973;
int t;
int m;
int x,y;
int k;
int Pow(int a,int b,int p)
{
int res=1;
int base=(a%p);
while(b)
{
if(b&1)
{
res=(res*base)%p;
}
base=(base*base)%p;
b>>=1;
}
return res;
}
struct Martix{
int n, m;
int val[10][10];
void clear() { memset(val, 0, sizeof(val)); }
void init() {
clear();
for (int i = 0; i < n; i++) {
val[i][i] = 1;
}
}
Martix operator*(const Martix x) const {
Martix Res;
Res.n = n;
Res.m = x.m;
Res.clear();
for (int k = 0; k <m; k++) {
for (int i = 0; i < Res.n; i++) {
for (int j = 0; j < Res.m; j++) {
Res.val[i][j]=(Res.val[i][j]+val[i][k]*x.val[k][j])%MOD;
}
}
}
return Res;
}
}A;
Martix ppow(Martix Ad, int b) {
Martix Res;
Res=Ad;
Res.init();
Martix Base = Ad;
while (b) {
if (b & 1) {
Res = Res * Base;
}
Base = (Base * Base);
b >>= 1;
}
return Res;
}
int F(int x)
{
Martix IDSY=ppow(A,x);
int Res=0;
for(int i=0;i<m;i++)
{
Res=(Res+IDSY.val[i][i])%MOD;
}
return Res;
}
vector<pair<int,int> >Rec;
int Phi[205][205];
int Pri[205][205];
int Used[205];
int Res=0;
int n;
void dfs(int x)
{
if(x==Rec.size())
{
int d=1;
int phi=1;
for(int i=0;i<Rec.size();i++)
{
d=(d*Pri[i][Used[i]]);
phi=(phi*Phi[i][Used[i]])%MOD;
}
Res=(Res+((phi)%MOD*F((n/d)))%MOD)%MOD;
return;
}
int Lim=Rec[x].second;
for(int i=0;i<=Lim;i++)
{
Used[x]=i;
dfs(x+1);
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
Rec.clear();
scanf("%d %d %d",&n,&m,&k);
A.clear();
A.n=m;
A.m=m;
for(int i=1;i<=A.n;i++)
{
for(int j=1;j<=A.n;j++)
{
A.val[i-1][j-1]=1;
}
}
for(int i=1;i<=k;i++)
{
scanf("%d %d",&x,&y);
A.val[x-1][y-1]=0;
A.val[y-1][x-1]=0;
}
Res=0;
int now=n;
for(int d=2;d*d<=now;d++)
{
if(now%d==0)
{
int Tot=0;
while(now%d==0)
{
now/=d;
Tot++;
}
Rec.push_back(make_pair(d,Tot));
}
}
if(now>1)
{
Rec.push_back(make_pair(now,1));
}
for(int i=0;i<Rec.size();i++)
{
int Lim=Rec[i].second;
int p=Rec[i].first;
Phi[i][0]=1;
Pri[i][0]=1;
for(int j=1;j<=Lim;j++)
{
Pri[i][j]=Pri[i][j-1]*p;
Phi[i][j]=Pri[i][j]-Pri[i][j-1];
}
}
dfs(0);
Res=(Res*Pow(n,MOD-2,MOD))%MOD;
printf("%d\n",Res);
}
return 0;
}
[MtOI2018]魔力环
wkr 希望能够得到一个由 \(n\) 个魔力珠串成的环。不过他对普通的环并不感兴趣,因此他提出了如下的要求:
- wkr 希望在这个环上,恰好有 \(m\) 个黑色的魔力珠与 \(n - m\) 个白色的魔力珠。
- 由于 wkr 认为黑色魔力珠不应过于密集,因此 wkr 希望这个环上不会出现一段连续的黑色魔力珠,其长度超过 \(k\)。
在 wkr 的心目中,满足上述要求的环才是美妙的。
不过这样的环可能并不唯一。 wkr 想要知道共有多少种不同的环满足他所提出的要求。然而 wkr 并不喜欢计算,他希望聪明的你能够告诉他答案。
在这里,我们认为两个环是不同的,当且仅当其中一个环仅通过旋转无法得到另一个环。
由于答案可能过大,因此输出答案对 \(998, 244, 353\) 取模后的结果。
沿用上一题的思路
\([A/G]=\dfrac{1}{n}\sum\limits_{g\in G}|A^g|=\dfrac{1}{n}\sum\limits_{d|n}f(d)\phi(\dfrac{n}{d})\),\(f(d)\)为长度为\(d\)时的答案,此时黑色点的数量为\(cb=(\dfrac{md}{(n)})\),这就要求\(cb\)是\(\dfrac{n}{d}\)的倍数,\(cw=x-cb\)为白色点的数目
考虑先构造一个点数为\(cw\)的环,然后考虑向里面插入\(cb\)个点且满足每个空隙的黑点数量不超过\(k\)
计数带标号,所以断环为链,枚举断点的黑点数量\(i\)
然后考虑剩下的链实际就是\(cw-1\)个盒子,\(cb-i\)个球,每个盒子不能超过\(k\)个球的方案
则\(F(d)=\sum\limits_{i=0}^{min(k,cb)}(i+1)\sum\limits_{j=0}^{n}\binom{cw-1}{j}\binom{cw-1+cb-i-1-kj}{cw-1-1}\)
#include<bits/stdc++.h>
using namespace std;
const int MOD=998244353;
const int MAXN=1e5+5;
int n;
int m;
int k;
int Pow(int a,int b,int p)
{
int res=1;
int base=(a%p);
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int gcd(int a,int b)
{
if(b==0)
{
return a;
}
return gcd(b,a%b);
}
int fac[MAXN];
int inv_fac[MAXN];
int C(int n, int m) {
if(m<0||m>n)
{
return 0;
}
if (m == 0 || n == m)
return 1;
int k = fac[n];
int ans = ((long long)k*inv_fac[n - m])%MOD;
ans = ((long long)ans*inv_fac[m])%MOD;
return ans;
}
int Cal(int n,int m,int k)
{
int Rex=0;
if(m<0)
{
return 0;
}
for(int i=0;i<=n;i++)
{
if(m<(k+1)*i)
{
break;
}
int Con=((long long)C(n,i)*C(n+m-1-(k+1)*i,n-1))%MOD;
if(i&1)
{
Rex=((long long)Rex-Con+MOD)%MOD;
}
else
{
Rex=((long long)Rex+Con)%MOD;
}
//printf("%d %d %d %d?\n",Con,i,n+m-1-(k+1)*i,n-1);
}
return Rex;
}
int F(int x)
{
if(m%(n/x))
{
return 0;
}
int N=x;
int cb=(m/(n/x));
int cw=(x-cb);
if(cb<=k)
{
return C(N,cb);
}
int Res=0;
for(int i=0;i<=min(cb,k);i++)
{
Res=((long long)Res+((long long)(i+1)*Cal(cw-1,cb-i,k))%MOD)%MOD;
}
return Res;
}
vector<pair<int,int> >Rec;
int Phi[205][205];
int Pri[205][205];
int Used[205];
int Res=0;
void dfs(int x)
{
if(x==Rec.size())
{
int d=1;
int phi=1;
for(int i=0;i<Rec.size();i++)
{
d=(d*Pri[i][Used[i]]);
phi=(phi*Phi[i][Used[i]])%MOD;
}
Res=((long long)Res+((long long)(phi)*F(n/d))%MOD)%MOD;
return;
}
int Lim=Rec[x].second;
for(int i=0;i<=Lim;i++)
{
Used[x]=i;
dfs(x+1);
}
}
signed main()
{
fac[0] = 1;
for (int i = 1; i <= MAXN-5; i++) {
fac[i] = ((long long)fac[i - 1] * i)%MOD;
}
inv_fac[MAXN-5] = inv(fac[MAXN-5], MOD);
for (int i = MAXN-5 - 1; i >= 0; i--) {
inv_fac[i] = ((long long)inv_fac[i + 1] * (i + 1)) % MOD;
}
Rec.clear();
scanf("%d %d %d",&n,&m,&k);
Res=0;
int now=n;
for(int d=2;d*d<=now;d++)
{
if(now%d==0)
{
int Tot=0;
while(now%d==0)
{
now/=d;
Tot++;
}
Rec.push_back(make_pair(d,Tot));
}
}
if(now>1)
{
Rec.push_back(make_pair(now,1));
}
for(int i=0;i<Rec.size();i++)
{
int Lim=Rec[i].second;
int p=Rec[i].first;
Phi[i][0]=1;
Pri[i][0]=1;
for(int j=1;j<=Lim;j++)
{
Pri[i][j]=Pri[i][j-1]*p;
Phi[i][j]=Pri[i][j]-Pri[i][j-1];
}
}
dfs(0);
Res=((long long)Res*inv(n,MOD))%MOD;
printf("%d\n",Res);
return 0;
}
[SHOI2006] 有色图
如果一张无向完全图(完全图就是任意两个不同的顶点之间有且仅有一条边相连)的每条边都被染成了一种颜色,我们就称这种图为有色图。如果两张有色图有相同数量的顶点,而且经过某种顶点编号的重排,能够使得两张图对应的边的颜色是一样的,我们就称这两张有色图是同构的。以下两张图就是同构的,因为假如你把第一张图的顶点 \((1,2,3,4)\) 置换成第二张图的 \((4,3,2,1)\),就会发现它们是一样的。
你的任务是,对于计算所有顶点数为 \(n\),颜色种类不超过 \(m\) 的图,最多有几张是两两不同构的图。由于最后的答案会很大,你只要输出结论模 \(p\) 的余数就可以了(\(p\) 是一个质数)。
这里图的置换群\(G\)就是一个全排列,同样用\(Burnside\)
\([A/G]=\dfrac{1}{|G|}\sum\limits_{g\in G}|A^g|\),瓶颈还是在\(|A^g|\),注意\(|A^g|\)是不动的边集
还是给\(g\)分解成几个循环
如果\((u,v)\)是在同一个循环\(S\)
则一共有\(\lfloor\dfrac{|S|}{2}\rfloor\)种边的循环(考虑一个正\(|S|\)多边形按边所对应的角度分类)
如果\((u,v)\)不在同一个循环,分别在\(S_1,S_2\)
边\((u,v)\)会经过\(lcm(|S_1|,|S_2|)\)次转动后复原,也就是说\((u,v)\)所在的边集环大小\(lcm(|S_1|,|S_2|)\)
个数则为\(\dfrac{|S_1|\times|S_2|}{lcm(|S_1|,|S_2|)}=gcd(|S_1|,|S_2|)\)
设\(g\)的第\(i\)个轮换大小为\(b_i\)
因而\([A/G]=\dfrac{1}{|G|}\sum\limits_{g\in G}|A^g|=\dfrac{1}{n!}\sum\limits_{g\in G}m^{\sum\lfloor\frac{b_i}{2}\rfloor+\sum\limits_i\sum\limits_{j>i}gcd(i,j)}\)
如果我们分拆\(n\)得到\(b\),定义\(f(b)\)为轮换序列为\(b\)的\(g\)的个数
\([A/G]=\dfrac{1}{n!}\sum\limits_{b}m^{\sum\lfloor\frac{b_i}{2}\rfloor+\sum\limits_i\sum\limits_{j>i}gcd(i,j)}f(b)\)
考虑\(f(b)\)的计算
将带标号的排列分成大小为\(b_i\)几组\(\dfrac{n!}{\prod b_i!}\),再考虑组内顺序为\(\dfrac{b_i!}{b_i}\)
同时相同的\(b_i\)是无顺序的,还要乘上\(\dfrac1{v_{bi}!}\)
\(f(b)=\dfrac{n!}{\prod b_i\prod v_{b_i}!}\)
#include<bits/stdc++.h>
using namespace std;
int n,m,MOD;
int fac[60];
int inv_fac[60];
int Inv[60];
int Pow(int a,int b,int p)
{
int res=1;
int base=(a%p);
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int gcd(int a,int b)
{
if(b==0)
{
return a;
}
return gcd(b,a%b);
}
int b[60];
int v[60];
int Res=0;
int Gcd[65][65];
void dfs(int x,int Rest,int las)
{
if(Rest==0)
{
for(int i=1;i<=n;i++)
{
v[i]=0;
}
int Tc=0;
for(int i=1;i<=x;i++)
{
v[b[i]]++;
Tc=((long long)Tc+(b[i]/2))%(MOD-1);
}
for(int i=1;i<=x;i++)
{
for(int j=i+1;j<=x;j++)
{
Tc=((long long)Tc+Gcd[b[i]][b[j]])%(MOD-1);
}
}
int Con=Pow(m,Tc,MOD);
for(int i=1;i<=x;i++)
{
Con=((long long)Con*Inv[b[i]])%MOD;
}
for(int i=1;i<=n;i++)
{
Con=((long long)Con*inv_fac[v[i]])%MOD;
}
Res=((long long)Res+Con)%MOD;
return;
}
for(int i=las;i<=Rest;i++)
{
b[x+1]=i;
dfs(x+1,Rest-i,i);
}
}
int main()
{
scanf("%d %d %d",&n,&m,&MOD);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
Gcd[i][j]=gcd(i,j);
}
Inv[i]=inv(i,MOD);
}
fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = ((long long)fac[i - 1] * i)%MOD;
}
inv_fac[n] = inv(fac[n], MOD);
for (int i = n - 1; i >= 0; i--) {
inv_fac[i] = ((long long)inv_fac[i + 1] * (i + 1)) % MOD;
}
dfs(0,n,1);
printf("%d\n",Res);
}