【XSY3908】迷宫(置换,图论,计数dp)

题面

迷宫

题解

不妨把只由左向边形成的图称为 “左图”,那么 “右图” 的定义同理。

如果只从图论的角度推,还是能推出来很多东西的。

比如:

  • \(X=Y=Z=0\) 时,右图能任意连,而左图只能是一些环。
  • 否则,左图和右图都只能是环。

但你发现接下来很难做。

所以考虑用数学表示图论,这样就有更多性质了。

具体来说,我们把右图看成一个映射 \(f\),左图看成一个映射 \(g\),那么题目就是要求 \(f^Xgf^Ygf^Z=id\)

  • \(X=Y=Z=0\) 时,即为 \(g^2=id\),此时 \(f\) 任意取,\(g\) 要求是一个置换(双射)。
  • 否则,\(f\)\(g\) 都要求是个置换(双射)。

不出意外地,和我们刚刚图论推出来的结论相符。接下来考虑如何统计方案数。

我们先不讨论第一种情况(\(X=Y=Z=0\))如何统计,因为在统计第二种情况的方法能套用到第一种情况上(具体如何套用最后说),于是我们现在来讨论第二种情况。

我们先考虑对 \(f^Xgf^Ygf^Z=id\) 化简:(注意映射中是不满足交换律的,但由于幂的定义,有 \(f^af^b=f^{a+b}=f^bf^a\),这里 \(f\) 是一个映射)。

\[\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} \]

注意到知道了 \(f\)\(gf^Y\) 后可以唯一解出 \(g\),所以不妨令 \(g\gets gf^Y\),那么我们就是要求方程 \(g^2=f^{Y-X-Z}\) 的解的个数。

但是你发现 \(Y-X-Z\) 有可能是负数,这很不好处理。

但由于映射的逆是唯一的(或者说映射和它们的逆是一一对应的),而 \(g^2=f^{Y-X-Z}=(f^{-1})^{X+Z-Y}\),所以可知方程 \(g^2=f^{Y-X-Z}\) 和方程 \(g^2=f^{X+Z-Y}\) 的解的个数是相等的。

于是设 \(m=|Y-X-Z|\),那么我们求方程 \(g^2=f^m\) 的解的个数即可。

注意,对于一个方程 \(x^2=a\)(其中 \(x,a\) 都是映射且 \(a\) 已知),它的 \(x\) 的解并不是唯一或者唯二的。所以我们需要用 DP 来求解 \(g^2=f^m\)

具体怎么 DP 呢?以下我们都会把数学中的映射和图论相结合。我们先了解一下环分解:

环分解:对于置换 \(f\) 中的一个大小为 \(x\) 的环,它在 \(f^p\) 中将会被分解为 \(\gcd(x,p)\) 个大小均为 \(\dfrac{x}{\gcd(x,p)}\) 的环。

\(s=g^2=f^m\)。我们考虑枚举 \(s\) 中的环来 DP:设 \(dp(e,i)\) 表示已经考虑完了 \(s\) 中所有大小不超过 \(e\) 的环,而且 \(s\) 中当前有 \(i\) 个元素的方案数。那么 \(dp(n,n)\) 即为答案。

考虑从 \(dp(e-1,i)\) 向外转移:我们枚举大小为 \(e\) 的环的个数 \(t\),然后从 \(dp(e-1,i)\)\(dp(e,i+et)\) 转移。

发现转移过程中需要用到:

  • \(\operatorname{wayf}_{e}(t)\) 表示满足右述条件的、总点数(总元素个数)为 \(et\)\(f\) 的方案数:\(f^m\) 恰好是 \(t\) 个大小为 \(e\) 的环。
  • \(\operatorname{wayg}_e(t)\) 表示满足右述条件的、总点数(总元素个数)为 \(et\)\(g\) 的方案数:\(g^2\) 恰好是 \(t\) 个大小为 \(e\) 的环。

你发现这两个数组是很类似的,于是在代码实现中我们可以设置一个函数 \(\operatorname{getway}(p,e)\) 表示求出数组 \(\operatorname{way}(t)\) 表示满足右述条件的、总点数(总元素个数)为 \(et\)\(f\) 的方案数:\(f^p\) 恰好是 \(t\) 个大小为 \(e\) 的环。

那么我们 DP 到 \(dp(e,*)\) 时只需要 \(\operatorname{getway}(m,e)\) 求出 \(\operatorname{wayf}_{e}(t)\)\(\operatorname{getway}(2,e)\) 求出 \(\operatorname{wayg}_{e}(t)\) 即可。

那么现在考虑如何实现 \(\operatorname{getway}(p,e)\)。发现 \(\operatorname{way}(t)\) 可以看作是扔给你 \(t\) 个大小为 \(e\) 的环为 \(f^p\),然后问你能还原出多少种 \(f\)。(注意 \(f^p\) 就是 \(t\) 个大小为 \(e\) 的环,没有其他东西)

由于 \(f^p\) 中的环的大小都为 \(e\),而且这些环只能由环分解得到,那么我们先找到所有的 \(y\),满足 \(f\) 中大小为 \(y\) 的环会在 \(f^p\) 中分解为若干个大小为 \(e\) 的环,不妨把这些 \(y\) 构成的集合称作 \(Y\),设 \(Y\) 中的第 \(i\) 个元素为 \(Y_i\)

然后我们另设 \(h(i,k)\) 表示已经考虑完了 \(f\) 中大小为 \(Y_1,Y_2,\cdots,Y_k\) 的环,而且在 \(f^p\) 中已经用了 \(k\) 个环的 \(f\) 的方案数。

考虑从 \(h(i-1,k)\) 向外转移:

\(y=Y_i\),我们枚举 \(f\) 中大小为 \(y\) 的环的个数 \(\ell\),那么这之中的每一个环在 \(f^p\) 中都会分解为 \(\gcd(y,p)\) 个大小为 \(\dfrac{x}{\gcd(x,p)}=e\) 的环。设 \(g=\gcd(y,p)\),把 \(f\) 中这些大小为 \(y\) 的环称作 “大环”,\(f^p\) 中这些大小为 \(e\) 的环称作 “小环”,那么意思就是说,\(1\) 个大环会分解成 \(g\) 个小环,\(\ell\) 个大环会分解成 \(g\cdot \ell\) 个小环。

所以从 \(h(i-1,k)\)\(h(i,k+g\ell)\) 转移:

\[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} \]

在这里插入图片描述

参考这个图,我们来逐一解释:

  • \(\dbinom{k+g\ell}{k}\cdot h(i-1,k)\):我们首先在扔给我的这 \(k+g\ell\) 个小环中选出 \(k\) 个,钦定它们是由 \(f\) 中大小为 \(Y_{1\sim i-1}\) 的环分解而成的。那么这部分的贡献已经有了,为 \(h(i-1,k)\)

  • \(\operatorname{group}(g,\ell)\):这个函数的意思是把剩下的 \(g\ell\) 个小环分成 \(\ell\) 组、每组 \(g\) 个的方案数。然后我们钦定同一组里面的小环是由同一个大环分解而来的。(显然分组方式不同对应的 \(f\) 也不同)

    如何求 \(\operatorname{group}(g,\ell)\):我们可以先从 \(g\ell\) 个小环里面取出 \(g\) 个,然后再从 \(g(\ell-1)\) 个小环里面取出 \(g\) 个……最后注意到组与组之间的先后顺序是无关的,需要除以 \(\ell!\),所以:

    \[\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!} \]

  • \(\bigg(\big(g-1\big)!\ e^{g-1}\bigg)^{\ell}\):现在我们已经分好组了,但一组小环并不能唯一确定一个大环,所以我们要考虑一组 \(g\) 个大小为 \(e\) 的小环可能由几种不同的大环分解而成。

    我们先不妨给所有的小环一个标号,然后再给每一个小环上的每一个点一个标号。例如,第 \(i\) 个小环上的第 \(j\) 个点编号为 \((i,j)\)

    那么我们考虑大环中,\((1,1)\) 这个点之后连续 \(g-1\) 个点是什么(显然这 \(g-1\) 个点确定了整个大环也就确定了)。显然分解后这 \(g-1\) 个点所在的小环是不同的,所以我们先乘上 \((g-1)!\),确定下来这 \(g-1\) 个点每一个点在哪一个小环。然后对于每一个点,它可以在我们给它确定的小环的所有点中任取,所以还需要再乘一个 \(e^{g-1}\)

    那么一组点贡献的方案数为 \((g-1)!e^{g-1}\)\(\ell\) 组点贡献的方案数就是 \(\bigg(\big(g-1\big)!\ e^{g-1}\bigg)^{\ell}\)

那么我们就可以通过 DP 顺利求出 \(\operatorname{way}\) 数组了。

然后求 \(dp(e,i)\) 的方式其实和求 \(h(i,k)\) 的方式是类似的。只不过 \(h(i,k)\) 是把小环还原成大环,而从 \(dp(e-1,i)\) 转移到 \(dp(e,i+et)\) 时是把图上的点还原成小环,再扔到求出来的 \(\operatorname{wayf}\)\(\operatorname{wayg}\) 里面把小环还原成大环。

所以就不讲 \(dp(e,i)\) 的转移过程了,自己手推一下就好。

主要是我太懒了(((

时间复杂度 \(O(n^2\log n)\),但我不太会证/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
*/
posted @ 2022-10-30 14:02  ez_lcw  阅读(19)  评论(0编辑  收藏  举报