AGC019E Shuffle and Swap

Link
显然(A_i=B_i=0)的位置可以忽略。
对于所有(A_i=B_i=1)的位置,(A_i)被拿走后必须拿一个(1)回来。这样的(i)称作(1)类点。
对于所有(A_i=1,B_i=0)的位置,(A_i)被拿走后不能拿(1)回来,这样的(i)称作(2)类点。
对于所有(A_i=0,B_i=1)的位置,我们还必须拿一个(1)回来,这样的(i)称作(3)类点。
显然(2,3)类点数目相同,设有(p)个一类点,(q)个二/三类点。
那么我们重排之后连(a_i ightarrow b_i)的边,对应的(1)类点就是入度出度都为(1)(2)类点出度为(1)入度为(0)(3)类点入度为(1)出度为(0)
这样会形成(q)条起始端为(2)类点,终点端为(3)类点,中间都是(1)类点的链,剩下的(1)类点就随便成环。
(f_{i,j})表示要放(i)(1)类点,要成(j)条链的方案数。
我们认为初始的点无序,而链上的(1)类点有一定的顺序。
边界为(f_{0,j}=(j!)^2),即(2,3)类点随意排列然后直接配对的方案数。
转移分两种情况。
一是选一对(2,3)类点配对成一条新的链,我们可以在剩下的(j)(2,3)号点中各任选一个,即(j^2f_{i,j-1} ightarrow f_{i,j})
二是选一个(1)号点加到已有的链里面去,我们可以在剩下的(i)(1)类点钟任选一个,在(j)条链中任选一个放进去,即(ijf_{i-1,j} ightarrow f_{i,j})

#include<cstdio>
#include<cstring>
const int N=10007,P=998244353;
char s[N],t[N];int f[N][N],fac[N],ifac[N];
int inc(int a,int b){return a+=b-P,a+(a>>31&P);}
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int C(int n,int m){return mul(mul(fac[n],ifac[m]),ifac[n-m]);}
int main()
{
    scanf("%s%s",s+1,t+1),fac[0]=1;
    int n=strlen(s+1),p=0,q=0,ans=0;
    for(int i=1;i<=n;++i) p+=s[i]=='1'&&t[i]=='1',q+=s[i]=='1'&&t[i]=='0';
    for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    ifac[n]=pow(fac[n],P-2);
    for(int i=n;i;--i) ifac[i-1]=mul(ifac[i],i);
    for(int i=0;i<=q;++i) f[0][i]=mul(fac[i],fac[i]);
    for(int i=1;i<=p;++i) for(int j=1;j<=q;++j) f[i][j]=inc(mul(mul(j,j),f[i][j-1]),mul(mul(i,j),f[i-1][j]));
    for(int i=0;i<=p;++i) ans=inc(ans,mul(mul(mul(mul(f[p-i][q],fac[i]),fac[i]),C(p,i)),C(p+q,i)));
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12264625.html