[XSY 3322][置换+dp]Dexterity

本文探讨了一种特殊的置换问题,当A=B=C=0时,求解满足特定条件的置换R,并通过动态规划(DP)方法进行求解。当A、B、C中至少有一个不为0时,解题策略转变为寻找满足特定矩阵关系的置换对L和R。通过建立递推公式和转化问题,最终得出解决方案,时间复杂度为O(Tn^2 log^2 n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们很容易发现。R必须为置换。
当A=B=C=0的时候,我们对于L没有限制,只要求R是个置换。
并满足R2=ER^2=ER2=E,采用dp。fi=fi−1+(i−1)∗fi−2f_i=f_{i-1}+(i-1)*f_{i-2}fi=fi1+(i1)fi2
最后答案即为fn∗nnf_n*n^nfnnn
当A,B,C中有一个不为0的时候,L,R都是置换。
并且它们满足式子:
             LARLBRLC=E\ \ \ \ \ \ \ \ \ \ \ \ \ L^ARL^BRL^C=E             LARLBRLC=E
 LCLARLBRLCL−C=E\ L^CL^ARL^BRL^CL^{-C}=E LCLARLBRLCLC=E
               LA+CRLBR=E\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ L^{A+C}RL^BR=E               LA+CRLBR=E
LA+C−BLBRLBR=EL^{A+C-B}L^BRL^BR=ELA+CBLBRLBR=E
注意到,当我们确定了L,我们想要LBRL^BRLBR是任何置换都行。
因此,求出满足条件的L,R的方案数等价于求满足
LA+C−BR2=EL^{A+C-B}R^2=ELA+CBR2=E
     LA+C−B=R−2\ \ \ \ \ L^{A+C-B}=R^{-2}     LA+CB=R2的置换
类似的,上面式子等价于
L∣A+C−B∣=R2L^{|A+C-B|}=R^2LA+CB=R2
我们相当于要求出La=R2L^a=R^2La=R2
接下来我们采用dp解决。
直接解决有点难,我们考虑间接解决。
Z=La=R2Z=L^a=R^2Z=La=R2
我们求出LaL^aLaZZZ的方案数t1,
再求出ZZZ分解成R2R^2R2的方案数t2,Ans+=t1∗t2Ans+=t1*t2Ans+=t1t2
我们令fi,jf_{i,j}fi,j表示ZZZ中j个长度为i的环,LaL^aLa有多少种方法可以达到。
注意到,LLL中一个长度为lll的环,会被分解成gcd(a,l)gcd(a,l)gcd(a,l)个长度为lgcd(a,l)\frac{l}{gcd(a,l)}gcd(a,l)l的环。
那么,我们枚举第一个点原来在那个大环,有递推式:
fi,j=∑k=1i∗j[kgcd(k,a)==i](i∗j−1)!i∗(j−gcd(k,a))!fi,j−gcd(k,a)f_{i,j}=\sum_{k=1}^{i*j}[\frac{k}{gcd(k,a)}==i]\frac{(i*j-1)!}{i*(j-gcd(k,a))!}f_{i,j-gcd(k,a)}fi,j=k=1ij[gcd(k,a)k==i]i(jgcd(k,a))!(ij1)!fi,jgcd(k,a)
我们令gi,jg_{i,j}gi,j表示ZZZ中j个长度为i的环有多少中方式分解成R2R^2R2
注意,这个时候已经确定了次序。
我们有递推式:gi,j=gi,j−1+(j−1)∗i∗gi,j−2g_{i,j}=g_{i,j-1}+(j-1)*i*g_{i,j-2}gi,j=gi,j1+(j1)igi,j2(想一想,为什么)
最后,我们令hi,j=fi,j∗gi,jh_{i,j}=f_{i,j}*g_{i,j}hi,j=fi,jgi,j
背包合并一下即可。
时间复杂度:O(Tn2log2n)O(Tn^2log_2n)O(Tn2log2n)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int Mod=998244353;
typedef unsigned long long ull;
int n;
ull A,B,C;
#define Maxn 1005
int sum[Maxn];
int fact[Maxn],inv[Maxn];
vector<int> vec[Maxn];
int f[Maxn][Maxn],g[Maxn][Maxn];
int way[Maxn],Ans[Maxn];
int c[Maxn][Maxn];
 
ull gcd(ull a,ull b){
    if(!b)return a;
    return gcd(b,a%b);
}
 
inline int Fast_Pow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%Mod;
        a=1ll*a*a%Mod;
        b>>=1;
    }
    return ans;
}
 
int main(){
    c[0][0]=1;
    for(register int i=1;i<=1000;++i){
        c[i][0]=1;
        for(register int j=1;j<=i;++j){
            c[i][j]=c[i-1][j]+c[i-1][j-1];
            if(c[i][j]>=Mod)c[i][j]-=Mod;
        }
    }
    for(register int i=1;i<=1000;++i){
        g[i][0]=1;
        g[i][1]=i&1;
        for(register int j=2;j<=1000;++j)g[i][j]=(1ll*g[i][j-1]*(i&1)+1ll*g[i][j-2]*(j-1)*i)%Mod;
    }
    fact[0]=1;
    for(int i=1;i<=1000;++i)fact[i]=1ll*fact[i-1]*i%Mod;
    inv[0]=inv[1]=1;
    for(int i=2;i<=1000;++i)inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
    for(int i=2;i<=1000;++i)inv[i]=1ll*inv[i-1]*inv[i]%Mod;
    int T;
    scanf("%d",&T);
    while(T--){
        memset(Ans,0,sizeof(Ans));
        memset(f,0,sizeof(f));
        scanf("%d%llu%llu%llu",&n,&A,&B,&C);
        for(register int i=1;i<=n;++i)vec[i].clear();
        if(A==0&&B==0&&C==0){
            sum[0]=1;
            for(register int i=1;i<=n;++i){
                sum[i]=sum[i-1];
                if(i>1)sum[i]=(sum[i]+1ll*(i-1)*sum[i-2])%Mod;
            }
            printf("%d\n",1ll*sum[n]*Fast_Pow(n,n)%Mod);
            continue;
        }
        A=A+C;
        if(A>=B)A-=B;
        else A=B-A;
        for(register int i=1;i<=n;++i){
        	int tmp=gcd(A,i);
        	vec[i/tmp].push_back(tmp);
        }
        Ans[0]=1;
        for(register int i=1;i<=n;++i){
            f[i][0]=1;
            for(register int j=1;j<=n/i;++j){
                for(register int k=0;k<vec[i].size();++k)
                    if(j>=vec[i][k])
                        f[i][j]=(f[i][j]+1ll*f[i][j-vec[i][k]]*fact[i*j-1]%Mod*inv[i*(j-vec[i][k])])%Mod;
                    else break;
            }
            for(register int j=0;j<=n/i;++j)way[j]=1ll*f[i][j]*g[i][j]%Mod;
            for(register int j=n;j>=0;--j){
                int ans=0;
                for(register int k=0;k*i<=j;++k)
                    ans=(ans+1ll*Ans[j-k*i]*way[k]%Mod*c[j][i*k])%Mod;
                Ans[j]=ans;
            }
        }
        printf("%d\n",Ans[n]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值