题面
题解
不妨把只由左向边形成的图称为 “左图”,那么 “右图” 的定义同理。
如果只从图论的角度推,还是能推出来很多东西的。
比如:
- 当 X = Y = Z = 0 X=Y=Z=0 X=Y=Z=0 时,右图能任意连,而左图只能是一些环。
- 否则,左图和右图都只能是环。
但你发现接下来很难做。
所以考虑用数学表示图论,这样就有更多性质了。
具体来说,我们把右图看成一个映射 f f f,左图看成一个映射 g g g,那么题目就是要求 f X g f Y g f Z = i d f^Xgf^Ygf^Z=id fXgfYgfZ=id。
- 当 X = Y = Z = 0 X=Y=Z=0 X=Y=Z=0 时,即为 g 2 = i d g^2=id g2=id,此时 f f f 任意取, g g g 要求是一个置换(双射)。
- 否则, f f f 和 g g g 都要求是个置换(双射)。
不出意外地,和我们刚刚图论推出来的结论相符。接下来考虑如何统计方案数。
我们先不讨论第一种情况( X = Y = Z = 0 X=Y=Z=0 X=Y=Z=0)如何统计,因为在统计第二种情况的方法能套用到第一种情况上(具体如何套用最后说),于是我们现在来讨论第二种情况。
我们先考虑对 f X g f Y g f Z = i d f^Xgf^Ygf^Z=id fXgfYgfZ=id 化简:(注意映射中是不满足交换律的,但由于幂的定义,有 f a f b = f a + b = f b f a f^af^b=f^{a+b}=f^bf^a fafb=fa+b=fbfa,这里 f f f 是一个映射)。
f X g f Y g f Z = i d f X g f Y g f Z f Y = f Y f X g f Y g f Y f Z = f Y f X ( g f Y ) 2 f Z = f Y ( g f Y ) 2 = f − X f Y f − Z = f Y − X − Z \begin{aligned} f^Xgf^Ygf^Z&=id\\ f^Xgf^Ygf^Zf^Y&=f^Y\\ f^Xgf^Ygf^Yf^Z&=f^Y\\ f^X\left(gf^Y\right)^2f^Z&=f^Y\\ \left(gf^Y\right)^2&=f^{-X}f^Yf^{-Z}=f^{Y-X-Z} \end{aligned} fXgfYgfZfXgfYgfZfYfXgfYgfYfZfX(gfY)2fZ(gfY)2=id=fY=fY=fY=f−XfYf−Z=fY−X−Z
注意到知道了 f f f 和 g f Y gf^Y gfY 后可以唯一解出 g g g,所以不妨令 g ← g f Y g\gets gf^Y g←gfY,那么我们就是要求方程 g 2 = f Y − X − Z g^2=f^{Y-X-Z} g2=fY−X−Z 的解的个数。
但是你发现 Y − X − Z Y-X-Z Y−X−Z 有可能是负数,这很不好处理。
但由于映射的逆是唯一的(或者说映射和它们的逆是一一对应的),而 g 2 = f Y − X − Z = ( f − 1 ) X + Z − Y g^2=f^{Y-X-Z}=(f^{-1})^{X+Z-Y} g2=fY−X−Z=(f−1)X+Z−Y,所以可知方程 g 2 = f Y − X − Z g^2=f^{Y-X-Z} g2=fY−X−Z 和方程 g 2 = f X + Z − Y g^2=f^{X+Z-Y} g2=fX+Z−Y 的解的个数是相等的。
于是设 m = ∣ Y − X − Z ∣ m=|Y-X-Z| m=∣Y−X−Z∣,那么我们求方程 g 2 = f m g^2=f^m g2=fm 的解的个数即可。
注意,对于一个方程 x 2 = a x^2=a x2=a(其中 x , a x,a x,a 都是映射且 a a a 已知),它的 x x x 的解并不是唯一或者唯二的。所以我们需要用 DP 来求解 g 2 = f m g^2=f^m g2=fm。
具体怎么 DP 呢?以下我们都会把数学中的映射和图论相结合。我们先了解一下环分解:
环分解:对于置换 f f f 中的一个大小为 x x x 的环,它在 f p f^p fp 中将会被分解为 gcd ( x , p ) \gcd(x,p) gcd(x,p) 个大小均为 x gcd ( x , p ) \dfrac{x}{\gcd(x,p)} gcd(x,p)x 的环。
设 s = g 2 = f m s=g^2=f^m s=g2=fm。我们考虑枚举 s s s 中的环来 DP:设 d p ( e , i ) dp(e,i) dp(e,i) 表示已经考虑完了 s s s 中所有大小不超过 e e e 的环,而且 s s s 中当前有 i i i 个元素的方案数。那么 d p ( n , n ) dp(n,n) dp(n,n) 即为答案。
考虑从 d p ( e − 1 , i ) dp(e-1,i) dp(e−1,i) 向外转移:我们枚举大小为 e e e 的环的个数 t t t,然后从 d p ( e − 1 , i ) dp(e-1,i) dp(e−1,i) 向 d p ( e , i + e t ) dp(e,i+et) dp(e,i+et) 转移。
发现转移过程中需要用到:
- wayf e ( t ) \operatorname{wayf}_{e}(t) wayfe(t) 表示满足右述条件的、总点数(总元素个数)为 e t et et 的 f f f 的方案数: f m f^m fm 恰好是 t t t 个大小为 e e e 的环。
- wayg e ( t ) \operatorname{wayg}_e(t) wayge(t) 表示满足右述条件的、总点数(总元素个数)为 e t et et 的 g g g 的方案数: g 2 g^2 g2 恰好是 t t t 个大小为 e e e 的环。
你发现这两个数组是很类似的,于是在代码实现中我们可以设置一个函数 getway ( p , e ) \operatorname{getway}(p,e) getway(p,e) 表示求出数组 way ( t ) \operatorname{way}(t) way(t) 表示满足右述条件的、总点数(总元素个数)为 e t et et 的 f f f 的方案数: f p f^p fp 恰好是 t t t 个大小为 e e e 的环。
那么我们 DP 到 d p ( e , ∗ ) dp(e,*) dp(e,∗) 时只需要 getway ( m , e ) \operatorname{getway}(m,e) getway(m,e) 求出 wayf e ( t ) \operatorname{wayf}_{e}(t) wayfe(t), getway ( 2 , e ) \operatorname{getway}(2,e) getway(2,e) 求出 wayg e ( t ) \operatorname{wayg}_{e}(t) wayge(t) 即可。
那么现在考虑如何实现 getway ( p , e ) \operatorname{getway}(p,e) getway(p,e)。发现 way ( t ) \operatorname{way}(t) way(t) 可以看作是扔给你 t t t 个大小为 e e e 的环为 f p f^p fp,然后问你能还原出多少种 f f f。(注意 f p f^p fp 就是 t t t 个大小为 e e e 的环,没有其他东西)
由于 f p f^p fp 中的环的大小都为 e e e,而且这些环只能由环分解得到,那么我们先找到所有的 y y y,满足 f f f 中大小为 y y y 的环会在 f p f^p fp 中分解为若干个大小为 e e e 的环,不妨把这些 y y y 构成的集合称作 Y Y Y,设 Y Y Y 中的第 i i i 个元素为 Y i Y_i Yi。
然后我们另设 h ( i , k ) h(i,k) h(i,k) 表示已经考虑完了 f f f 中大小为 Y 1 , Y 2 , ⋯ , Y k Y_1,Y_2,\cdots,Y_k Y1,Y2,⋯,Yk 的环,而且在 f p f^p fp 中已经用了 k k k 个环的 f f f 的方案数。
考虑从 h ( i − 1 , k ) h(i-1,k) h(i−1,k) 向外转移:
令 y = Y i y=Y_i y=Yi,我们枚举 f f f 中大小为 y y y 的环的个数 ℓ \ell ℓ,那么这之中的每一个环在 f p f^p fp 中都会分解为 gcd ( y , p ) \gcd(y,p) gcd(y,p) 个大小为 x gcd ( x , p ) = e \dfrac{x}{\gcd(x,p)}=e gcd(x,p)x=e 的环。设 g = gcd ( y , p ) g=\gcd(y,p) g=gcd(y,p),把 f f f 中这些大小为 y y y 的环称作 “大环”, f p f^p fp 中这些大小为 e e e 的环称作 “小环”,那么意思就是说, 1 1 1 个大环会分解成 g g g 个小环, ℓ \ell ℓ 个大环会分解成 g ⋅ ℓ g\cdot \ell g⋅ℓ 个小环。
所以从
h
(
i
−
1
,
k
)
h(i-1,k)
h(i−1,k) 向
h
(
i
,
k
+
g
ℓ
)
h(i,k+g\ell)
h(i,k+gℓ) 转移:
h
(
i
,
k
+
g
ℓ
)
+
=
(
k
+
g
ℓ
k
)
⋅
h
(
i
−
1
,
k
)
⋅
group
(
g
,
ℓ
)
⋅
(
(
g
−
1
)
!
e
g
−
1
)
ℓ
h(i,k+g\ell)\ +\!\!=\ \dbinom{k+g\ell}{k}\cdot h(i-1,k)\cdot \operatorname{group}(g,\ell) \cdot \bigg(\big(g-1\big)!\ e^{g-1}\bigg)^{\ell}
h(i,k+gℓ) += (kk+gℓ)⋅h(i−1,k)⋅group(g,ℓ)⋅((g−1)! eg−1)ℓ
参考这个图,我们来逐一解释:
-
( k + g ℓ k ) ⋅ h ( i − 1 , k ) \dbinom{k+g\ell}{k}\cdot h(i-1,k) (kk+gℓ)⋅h(i−1,k):我们首先在扔给我的这 k + g ℓ k+g\ell k+gℓ 个小环中选出 k k k 个,钦定它们是由 f f f 中大小为 Y 1 ∼ i − 1 Y_{1\sim i-1} Y1∼i−1 的环分解而成的。那么这部分的贡献已经有了,为 h ( i − 1 , k ) h(i-1,k) h(i−1,k)。
-
group ( g , ℓ ) \operatorname{group}(g,\ell) group(g,ℓ):这个函数的意思是把剩下的 g ℓ g\ell gℓ 个小环分成 ℓ \ell ℓ 组、每组 g g g 个的方案数。然后我们钦定同一组里面的小环是由同一个大环分解而来的。(显然分组方式不同对应的 f f f 也不同)
如何求 group ( g , ℓ ) \operatorname{group}(g,\ell) group(g,ℓ):我们可以先从 g ℓ g\ell gℓ 个小环里面取出 g g g 个,然后再从 g ( ℓ − 1 ) g(\ell-1) g(ℓ−1) 个小环里面取出 g g g 个……最后注意到组与组之间的先后顺序是无关的,需要除以 ℓ ! \ell! ℓ!,所以:
group ( g , ℓ ) = ( g ℓ g , g , ⋯ , g ) ℓ ! = ( g ℓ ) ! g ! g ! ⋯ g ! ℓ ! = ( g ℓ ) ! ( g ! ) ℓ ⋅ ℓ ! \operatorname{group}(g,\ell)=\dfrac{\dbinom{g\ell}{g,g,\cdots,g}}{\ell!}=\dfrac{\dfrac{(g\ell)!}{g!g!\cdots g!}}{\ell!}=\dfrac{(g\ell)!}{(g!)^{\ell}\cdot \ell!} group(g,ℓ)=ℓ!(g,g,⋯,ggℓ)=ℓ!g!g!⋯g!(gℓ)!=(g!)ℓ⋅ℓ!(gℓ)!
-
( ( g − 1 ) ! e g − 1 ) ℓ \bigg(\big(g-1\big)!\ e^{g-1}\bigg)^{\ell} ((g−1)! eg−1)ℓ:现在我们已经分好组了,但一组小环并不能唯一确定一个大环,所以我们要考虑一组 g g g 个大小为 e e e 的小环可能由几种不同的大环分解而成。
我们先不妨给所有的小环一个标号,然后再给每一个小环上的每一个点一个标号。例如,第 i i i 个小环上的第 j j j 个点编号为 ( i , j ) (i,j) (i,j)。
那么我们考虑大环中, ( 1 , 1 ) (1,1) (1,1) 这个点之后连续 g − 1 g-1 g−1 个点是什么(显然这 g − 1 g-1 g−1 个点确定了整个大环也就确定了)。显然分解后这 g − 1 g-1 g−1 个点所在的小环是不同的,所以我们先乘上 ( g − 1 ) ! (g-1)! (g−1)!,确定下来这 g − 1 g-1 g−1 个点每一个点在哪一个小环。然后对于每一个点,它可以在我们给它确定的小环的所有点中任取,所以还需要再乘一个 e g − 1 e^{g-1} eg−1。
那么一组点贡献的方案数为 ( g − 1 ) ! e g − 1 (g-1)!e^{g-1} (g−1)!eg−1, ℓ \ell ℓ 组点贡献的方案数就是 ( ( g − 1 ) ! e g − 1 ) ℓ \bigg(\big(g-1\big)!\ e^{g-1}\bigg)^{\ell} ((g−1)! eg−1)ℓ。
那么我们就可以通过 DP 顺利求出 way \operatorname{way} way 数组了。
然后求 d p ( e , i ) dp(e,i) dp(e,i) 的方式其实和求 h ( i , k ) h(i,k) h(i,k) 的方式是类似的。只不过 h ( i , k ) h(i,k) h(i,k) 是把小环还原成大环,而从 d p ( e − 1 , i ) dp(e-1,i) dp(e−1,i) 转移到 d p ( e , i + e t ) dp(e,i+et) dp(e,i+et) 时是把图上的点还原成小环,再扔到求出来的 wayf \operatorname{wayf} wayf 和 wayg \operatorname{wayg} wayg 里面把小环还原成大环。
所以就不讲 d p ( e , i ) dp(e,i) dp(e,i) 的转移过程了,自己手推一下就好。
主要是我太懒了(((
时间复杂度 O ( n 2 log n ) O(n^2\log n) O(n2logn),但我不太会证/kk
代码如下:
#include<bits/stdc++.h>
#define N 1010
#define re register
#define ll long long
using namespace std;
namespace modular
{
const int mod=1000000007;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void Add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
}using namespace modular;
inline int poww(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
int n;
int fac[N],ifac[N],inv[N];
int f[N],g[N],dp[N][N];
void init()
{
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=mul(fac[i-1],i);
ifac[n]=poww(fac[n],mod-2);
for(int i=n;i>=1;i--)
ifac[i-1]=mul(ifac[i],i);
for(int i=1;i<=n;i++)
inv[i]=poww(i,mod-2);
}
inline int C(int n,int m)
{
return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
inline int group(int g,int l)
{
return mul(fac[g*l],mul(poww(ifac[g],l),ifac[l]));
}
void getway(ll p,int e,int *ans)
{
static int dp[N][N],Y[N];
Y[0]=0;
for(int y=1;y<=n;y++)
if(gcd(y,p)*e==y) Y[++Y[0]]=y;
int K=n/e;
for(re int i=0;i<=Y[0];i++)
for(re int j=0;j<=K;j++)
dp[i][j]=0;
dp[0][0]=1;
for(re int i=1;i<=Y[0];i++)
{
const int y=Y[i],g=gcd(y,p);
const int tmp=mul(poww(e,g-1),fac[g-1]);
for(re int k=0;k<=K;k++)
{
int prod=1;
for(re int l=0;k+l*g<=K;l++)
{
if(dp[i-1][k]) Add(dp[i][k+l*g],mul(mul(C(k+l*g,k),dp[i-1][k]),mul(group(g,l),prod)));
prod=mul(prod,tmp);
}
}
}
for(int i=0;i<=K;i++)
ans[i]=dp[Y[0]][i];
}
int main()
{
ll X,Y,Z;
scanf("%d%lld%lld%lld",&n,&X,&Y,&Z);
init();
if(X+Y+Z==0)
{
if(n&1) puts("0");
else
{
getway(2,1,f);
printf("%d\n",mul(f[n],poww(n,n)));
}
return 0;
}
ll m=abs(X-Y+Z);
dp[0][0]=1;
for(re int e=1;e<=n;e++)
{
getway(m,e,f);
getway(2,e,g);
for(re int k=0;k<=n;k++)
{
int prod=1;
for(re int l=0;k+l*e<=n;l++)
{
if(dp[e-1][k]) Add(dp[e][k+l*e],mul(mul(C(k+l*e,k),dp[e-1][k]),mul(mul(group(e,l),prod),mul(f[l],g[l]))));
prod=mul(prod,fac[e-1]);
}
}
}
printf("%d\n",dp[n][n]);
return 0;
}
/*
3 1 0 1
*/
/*
5 8 5 8
*/