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\rightarrow 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}\rightarrow f_{i,j}\)
二是选一个\(1\)号点加到已有的链里面去,我们可以在剩下的\(i\)\(1\)类点钟任选一个,在\(j\)条链中任选一个放进去,即\(ijf_{i-1,j}\rightarrow 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);
}

猜你喜欢

转载自www.cnblogs.com/cjoierShiina-Mashiro/p/12264625.html