• [AGC019E]Shuffle and Swap


    [AGC019E]Shuffle and Swap

    题目大意:

    给出两个长度为(n(nle10000))(01)(A_{1sim n})(B_{1sim n})。两个串均有(k)'1'。令(a_{1sim k})(b_{1sim k})分别表示(A)(B)中所有'1'出现的位置。将(a)(b)等概率随机排列,按(1sim k)的顺序交换(A_{a_i})(A_{b_i})。令(P)表示操作完成后(A)(B)相等的概率,求(P imes(k!)^2)在模(998244353)意义下的值。

    思路:

    (k)'1'分为两类:(S_1)(S_2)(S_1)表示(A)(B)中均为'1',有(s_1)个;(S_2)表示(A)(B)中只有一个为'1',有(s_2)个。用(f[i][j])表示(S_1)交换完(i)个,(S_2)交换完(j)个后的方案数。

    显然,初始情况下,(S_1)可以交换(2)次,(S_2)可以交换(1)次。方便起见,假设我们的(A)(B)分别为:

    110011
    111100
    

    对于(i=0)的初始情况,由于(S_2)之间任意配对都可以使得(A=B),因此有((j!)^2)种方案。

    若我们交换一对(S_2),如(4)(5),那么交换后,可以交换(1)次的(S_2)减少了一对。有转移(f[i][j]+=f[i][j-1] imes j^2)

    若我们交换一个(S_1)和一个(S_2),如(2)(6),那么交换后,原来那个(S_2)已经不能再交换了,而原来可以交换(2)次的(S_1)现在只能交换一次,可以算入(S_2)中,也就是相当于减少了一个(S_1)。有转移(f[i][j]+=f[i-1][j] imes i imes j)

    最后统计答案时,对于(S_1)没有用完的情况,无论如何配对都能满足(A=B)。因此(f[s_1-i][s_2])对答案的贡献为(f[s_1-i][s_2] imes(i!)^2 imesinom{s_1}i imesinom ki)

    时间复杂度(mathcal O(n^2))

    源代码:

    #include<cstdio>
    #include<cstring>
    using int64=long long;
    constexpr int N=10001,mod=998244353;
    char a[N],b[N];
    int fact[N],factinv[N],f[N][N];
    void exgcd(const int &a,const int &b,int &x,int &y) {
    	if(!b) {
    		x=1,y=0;
    		return;
    	}
    	exgcd(b,a%b,y,x);
    	y-=a/b*x;
    }
    inline int inv(const int &x) {
    	int ret,tmp;
    	exgcd(x,mod,ret,tmp);
    	return (ret%mod+mod)%mod;
    }
    inline int C(const int &n,const int &m) {
    	if(n<m||n<0||m<0) return 0;
    	return (int64)fact[n]*factinv[m]%mod*factinv[n-m]%mod;
    }
    int main() {
    	scanf("%s%s",a,b);
    	const int n=strlen(a);
    	for(register int i=fact[0]=1;i<=n;i++) {
    		fact[i]=(int64)fact[i-1]*i%mod;
    	}
    	factinv[n]=inv(fact[n]);
    	for(register int i=n;i;i--) {
    		factinv[i-1]=(int64)factinv[i]*i%mod;
    	}
    	int s1=0,s2=0;
    	for(register int i=0;i<n;i++) {
    		if(a[i]=='1'&&b[i]=='1') s1++;
    		if(a[i]=='1'&&b[i]=='0') s2++;
    	}
    	for(register int i=0;i<=s2;i++) {
    		f[0][i]=(int64)fact[i]*fact[i]%mod;
    	}
    	for(register int i=1;i<=s1;i++) {
    		for(register int j=1;j<=s2;j++) {
    			f[i][j]=((int64)f[i-1][j]*i%mod*j%mod+(int64)f[i][j-1]*j%mod*j%mod)%mod;
    		}
    	}
    	int ans=0;
    	for(register int i=0;i<=s1;i++) {
    		(ans+=(int64)f[s1-i][s2]*fact[i]%mod*fact[i]%mod*C(s1,i)%mod*C(s1+s2,i)%mod)%=mod;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    【Win 10 应用开发】获取本机的IP地址
    【Win 10应用开发】延迟共享
    【Win 10 应用开发】共享目标(UWP)
    【Win 10应用开发】响应系统回退键的导航事件
    编写Windows服务疑问2:探索服务与安装器的关系
    编写Windows服务疑问1:操作过程
    服务器常见错误代码500、501、502、503、504、505
    git reset与git revert的区别
    Redis集群方案怎么做?
    ThinkPHP设计模式与Trait技术
  • 原文地址:https://www.cnblogs.com/skylee03/p/9166950.html
Copyright © 2020-2023  润新知