题目链接
https://lydsy.com/JudgeOnline/problem.php?id=1488
https://lydsy.com/JudgeOnline/problem.php?id=1815
题解
考虑polya,对于一个点的置换
A
1
,
⋯
 
,
A
n
A_1,\cdots ,A_n
A1,⋯,An,假设循环节的长度分别为
L
1
,
⋯
 
,
L
m
L_1,\cdots ,L_m
L1,⋯,Lm,那么边置换的循环节个数为
k
=
∑
i
=
1
m
⌊
L
i
2
⌋
+
∑
i
=
1
m
∑
j
=
i
+
1
m
gcd
(
L
i
,
L
j
)
k=\sum_{i=1}^m \lfloor \frac{L_i}{2}\rfloor+\sum_{i=1}^m \sum_{j=i+1}^m \gcd(L_i,L_j)
k=i=1∑m⌊2Li⌋+i=1∑mj=i+1∑mgcd(Li,Lj)
解释:考虑点置换的循环节内的边,显然跨度为
x
x
x的都只能是一种颜色,又由于
x
x
x与
L
−
x
L-x
L−x本质相同,所以这些边的循环节个数为
⌊
L
2
⌋
\lfloor \frac{L}{2}\rfloor
⌊2L⌋个;考虑点的循环节之间的边,对于两个长度分别为
L
i
,
L
j
L_i,L_j
Li,Lj的循环节,边的循环节个数容(zhao)易(chu)证(gui)明(lv)是
gcd
(
L
i
,
L
j
)
\gcd(L_i,L_j)
gcd(Li,Lj)种。
假设现在找到了
L
1
,
⋯
 
,
L
m
L_1,\cdots,L_m
L1,⋯,Lm,其中
L
1
≤
L
2
≤
⋯
≤
L
m
L_1\leq L_2\leq \cdots\leq L_m
L1≤L2≤⋯≤Lm,满足循环节长度为
i
i
i的个数为
S
i
S_i
Si种,那么满足上述条件的点置换个数为
t
=
n
!
∏
L
i
∏
S
i
!
t=\frac{n!}{\prod L_i\prod S_i!}
t=∏Li∏Si!n!
解释:考虑将每个置换看作一个圆排列,将
n
n
n个点放入大小分别为
L
i
L_i
Li的集合方案数为
(
n
L
1
)
(
n
−
L
1
L
2
)
⋯
(
L
m
−
1
+
L
m
L
m
)
∏
S
i
!
=
n
!
∏
L
i
!
∏
S
i
!
\frac{\binom{n}{L_1}\binom{n-L_1}{L_2}\cdots\binom{L_{m-1}+L_m}{L_m}}{\prod{S_i!}}=\frac{n!}{\prod L_i!\prod S_i!}
∏Si!(L1n)(L2n−L1)⋯(LmLm−1+Lm)=∏Li!∏Si!n!
每个圆排列的方案数为
∏
(
L
i
−
1
)
!
\prod (L_i-1)!
∏(Li−1)!
因此答案就是
1
n
!
∑
t
C
k
\frac{1}{n!}\sum tC^k
n!1∑tCk
代码
BZOJ 1488
#include <cstdio>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int maxn=60;
const int mod=997;
int quickpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
{
res=res*a%mod;
}
a=a*a%mod;
b>>=1;
}
return res;
}
int n,l[maxn+2],tot,s[maxn+2],ans,inv[maxn+2],fac[maxn+2],ifac[maxn+2],g[maxn+2][maxn+2],power[maxn*maxn+2];
int getans()
{
int cnt=fac[n],totc=0;
for(int i=1; i<=tot; ++i)
{
cnt=cnt*inv[l[i]]%mod;
}
for(int i=1; i<=n; ++i)
{
cnt=cnt*ifac[s[i]]%mod;
}
for(int i=1; i<=tot; ++i)
{
totc+=l[i]/2;
}
for(int i=1; i<tot; ++i)
{
for(int j=i+1; j<=tot; ++j)
{
totc+=g[l[i]][l[j]];
}
}
ans=(ans+cnt*power[totc])%mod;
return 0;
}
int search(int now,int sum)
{
int last=tot;
if(now==1)
{
for(int i=1; i+sum<=n; ++i)
{
l[++tot]=1;
}
s[1]=n-sum;
getans();
tot=last;
return 0;
}
for(int i=0; sum<=n; ++i,++s[l[++tot]=now],sum+=now)
{
search(now-1,sum);
}
tot=last;
s[now]=0;
return 0;
}
int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
int main()
{
n=read();
if(n==0)
{
puts("1");
return 0;
}
inv[0]=inv[1]=1;
for(int i=2; i<=n; ++i)
{
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
fac[0]=1;
for(int i=1; i<=n; ++i)
{
fac[i]=fac[i-1]*i%mod;
}
ifac[0]=1;
for(int i=1; i<=n; ++i)
{
ifac[i]=ifac[i-1]*inv[i]%mod;
}
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
{
g[i][j]=gcd(i,j);
}
}
power[0]=1;
for(int i=1; i<=n*n; ++i)
{
power[i]=power[i-1]<<1;
if(power[i]>=mod)
{
power[i]-=mod;
}
}
search(n,0);
printf("%d\n",ans*ifac[n]%mod);
return 0;
}
BZOJ 1815
#include <cstdio>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int maxn=53;
int mod;
int quickpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
{
res=res*a%mod;
}
a=a*a%mod;
b>>=1;
}
return res;
}
int n,col,l[maxn+2],tot,s[maxn+2],ans,inv[maxn+2],fac[maxn+2],ifac[maxn+2],g[maxn+2][maxn+2],power[maxn*maxn+2];
int getans()
{
int cnt=fac[n],totc=0;
for(int i=1; i<=tot; ++i)
{
cnt=1ll*cnt*inv[l[i]]%mod;
}
for(int i=1; i<=n; ++i)
{
cnt=1ll*cnt*ifac[s[i]]%mod;
}
for(int i=1; i<=tot; ++i)
{
totc+=l[i]/2;
}
for(int i=1; i<tot; ++i)
{
for(int j=i+1; j<=tot; ++j)
{
totc+=g[l[i]][l[j]];
}
}
ans=(ans+1ll*cnt*power[totc])%mod;
return 0;
}
int search(int now,int sum)
{
int last=tot;
if(now==1)
{
for(int i=1; i+sum<=n; ++i)
{
l[++tot]=1;
}
s[1]=n-sum;
getans();
tot=last;
return 0;
}
for(int i=0; sum<=n; ++i,++s[l[++tot]=now],sum+=now)
{
search(now-1,sum);
}
tot=last;
s[now]=0;
return 0;
}
int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
int main()
{
n=read();
col=read();
mod=read();
if(n==0)
{
puts("1");
return 0;
}
inv[0]=inv[1]=1;
for(int i=2; i<=n; ++i)
{
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
fac[0]=1;
for(int i=1; i<=n; ++i)
{
fac[i]=1ll*fac[i-1]*i%mod;
}
ifac[0]=1;
for(int i=1; i<=n; ++i)
{
ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
}
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
{
g[i][j]=gcd(i,j);
}
}
power[0]=1;
for(int i=1; i<=n*n; ++i)
{
power[i]=1ll*power[i-1]*col%mod;
if(power[i]>=mod)
{
power[i]-=mod;
}
}
search(n,0);
printf("%lld\n",1ll*ans*ifac[n]%mod);
return 0;
}