[agc019e]Shuffle and Swap

前言

计数水平不行了。
居然不会n^2去dp这个模型。

题意

太麻烦了看网站。

DP

假设有x个公用1和y个非公用1。
用图论来理解。
最后一定形成y条链,若干个环。
其中链中的边在序列中要按顺序,环则随意。
环可以先不管。
考虑dp[i,j]表示目前做出来两个i+j的序列,有i个公用1和j个非公用1,区分编号,有顺序,形成j条链的方案数。
有dp[i,j]=dp[i-1,j]*i*j+dp[i,j-1]*j*j。
后面那个比较好理解,就是在最后添加一对非公用,有j^2种方法。(不考虑全局编号,只考虑局部问题的编号,因此目前编号是1~j,子问题编号是1~j-1,如果新的选择一个编号在1~j-1,那么让原本拥有这个编号的变成j即可,是唯一对应方案)。
前面那个呢?i同理是编号方案数,因为链有顺序,所以认为最后这个刚好加到某个链的尾部,然后可以把它和对应的非公用1所在位置调换,就是有顺序了。
最后枚举用了多少公用1来参与链的形成,剩余的随便搞(记得把公用1的编号分成两个集合分配,然后还有位置需要分配)。
复杂度是咸鱼的平方。

#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=10000+10,mo=998244353;
char s[maxn];
int f[maxn][maxn],fac[maxn],inv[maxn],bz[maxn];
int i,j,k,l,t,n,m,ans,x,y;
int qsm(int x,int y){
    if (!y) return 1;
    int t=qsm(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
int C(int n,int m){
    if (n<m||m<0) return 0;
    return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;
}
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    fo(i,1,n)
        if (s[i]=='1') y++,bz[i]++;
    scanf("%s",s+1);
    fo(i,1,n)
        if (s[i]=='1'&&bz[i]) x++;
    y-=x;
    fac[0]=1;
    fo(i,1,n) fac[i]=(ll)fac[i-1]*i%mo;
    inv[n]=qsm(fac[n],mo-2);
    fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;
    f[0][0]=1;
    fo(i,1,y) f[0][i]=(ll)fac[i]*fac[i]%mo;
    fo(i,1,x)
        fo(j,1,y)
            f[i][j]=(ll)((ll)f[i-1][j]*i%mo+(ll)f[i][j-1]*j%mo)*j%mo;
    fo(i,0,x)
        (ans+=(ll)f[x-i][y]*qsm(fac[i],2)%mo*C(x,i)%mo*C(x+y,i)%mo)%=mo;
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}

你可能感兴趣的:(一般动规与递推,排列组合)