• 组合数学之TwelveFold Way


    组合数学之TwelveFold Way

    题目传送


    LLC

    • n个球有标号,m个盒子有标号,每个盒子至多放一个球
    • 如果 (n>m,ans=0) - 否则为 (ans=A^n_m=C_m^ncdot A_n^n),相当于是先从 (m) 个盒子取出 (n)个盒子,再全排因为球有编号

    LLA

    • n个球有标号,m个盒子有标号,盒子没有限制 - 那么每个球都有 (m) 种选择 - (ans=m^n)

    LLB

    • n个球有编号,m个盒子有编号,每个盒子至少放一个球 - 如果 (n<m,ans=0)
    • 否则,我们强制有 (i) 个盒子为空,其他盒子乱放,需要容斥一下。
    • 首先向LLA一样,答案有 (m^n) ,但是这里边含有盒子为空的情况。把一个盒子为空两个盒子为空三个盒子为空...的情况都减掉,但是一个盒子为空里边又包含两个盒子为空三个盒子为空....的情况,会减多,我们加回来两个盒子为空的情况 $$ans=sum_{i=0}m(-1)i cdot C_m^i cdot (m-i)!$$

    LUC

    • n个球有标号,m个盒子无标号,每个盒子至多放一个球
    • 如果 (n>m,ans=0) - 否则为 (ans=1)

    LUB

    • n个球有标号,m个盒子无标号,每个盒子至少放一个球
    • 我们考虑 DP,设 (f[i][j]) 表示前 (i) 个球放在 (j) 个盒子里的方案数$$f[i][j]=f[i-1][j-1]+j cdot f[i-1][j]$$
    • 因为球有编号,这样顺序DP 起了去重的作用
    • 第二类斯特林数的递推公式 $$S_nm=s_{n-1}{m-1}+mcdot S_{n-1}^{m}$$

    LUA

    • n个球有标号,m个盒子无标号,盒子无限制 - 我们可以利用LUB,然后枚举空盒子的数量 $$ans=sum_{i=1}^mf[n][i]$$

    ULC

    • n个球无标号,m个盒子有标号,每个盒子至多放一个球
    • 如果 (n>m,ans=0) - 否则为 (ans=1)

    ULB

    • n个球无标号,m个盒子有标号,每个盒子至少放一个球
    • 隔板法 $$ans=C_{n-1}^{m-1}$$

    ULA

    • n个球无标号,m个盒子有标号,盒子无限制
    • 还是隔板法,不过强制几个盒子为空 $$ans=sum_{i=1}{m}C_{n-1}{i-1} cdot C_{m}^{i}$$
    • 不要忘记乘强制选盒子的方案数

    UUC

    • n个球无标号,m个盒子无标号,每个盒子至多放一个球
    • 如果 (n>m,ans=0)
    • 否则为 (ans=1)

    UUB

    • n个球无标号,m个盒子无标号,每个盒子至少放一个球
    • 这个不能用隔板法,因为划分出相同数量的盒子,盒子没有编号是相同的。
    • 我们考虑DP,设 (f[i][j]) 表示前 (i) 个球分到 (j) 个盒子里
    • 为了保证不重,我们保证盒子里球的数量单调不降
    • 转移 $$f[i][j]=f[i-1][j-1]+f[i-j][j-1]$$
    • 对于当前的球要么新开一个盒子,要么再之前的盒子基础上都放进去一个
    • 划分数

    UUA

    • 对UUB求个和就好了$$ans=sum_{i=1}^mf[n][i]$$

    代码

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define ll long long 
    #define mod 998244353
    using namespace std;
    string s;
    ll fac[1000005],f[1005][1005],infac[1000005];
    ll n,m;
    ll qpow(ll a,ll b){
    	ll ans=1ll,res=a;
    	while(b){
    		if(b&1) ans=(ans*res)%mod;
    		res=(res*res)%mod;
    		b>>=1;
    	}
    	return ans;
    }
    void pre(){
    	ll nn=max(n,m);
    //	nn=min(nn,1000000);
    	fac[0]=1ll;
    	for(ll i=1;i<=nn;i++) fac[i]=fac[i-1]*i*1ll%mod;
    	infac[nn]=qpow(fac[nn],mod-2);
    	
    	for(ll i=nn-1;i>=0;i--) infac[i]=infac[i+1]*(i+1)*1ll%mod;
    //	for(ll i=0;i<=nn;i++) printf("%lld ",infac[i]);
    //	cout<<qpow(fac[nn],2)<<endl;
    //	cout<<endl;
    }
    void work1(){
    	if(n<=m) printf("1
    ");
    	else printf("0
    ");
    	return ;
    }
    void work2(){//LLC 
    	if(n<=m) printf("%lld",fac[m]*infac[m-n]%mod);
    	else  printf("0
    ");
    //	else if(n==m) printf("")
    }
    void work3(){
    	if(n<=m) printf("%lld",fac[m]*infac[n]%mod*infac[m-n]%mod);
    	else printf("0
    ");
    }
    void work4(){//LLB 
    	if(n<m){
    		printf("0
    ");
    		return ;
    	}
    	ll ans=0;
    	for(ll i=0;i<=m;i++){
    		if(i%2==0) ans=(ans+fac[m]*infac[m-i]%mod*infac[i]%mod*qpow(m-i,n)%mod)%mod;
    		else ans=(ans+mod-fac[m]*infac[m-i]%mod*infac[i]%mod*qpow(m-i,n)%mod)%mod;
    	}
    	printf("%lld
    ",ans);
    }
    void work5(){//LUB 
    	if(n<m){
    		printf("0
    ");
    		return ;
    	}
    	f[0][0]=1;
    	//前 i个球放在 j个盒子里的方案数 
    	for(ll i=1ll;i<=n;i++)
    	  for(ll j=1ll;j<=m;j++)
    	    f[i][j]=(f[i-1][j-1]+j*f[i-1][j]%mod)%mod;
    	printf("%lld
    ",f[n][m]);
    }
    void work6(){//ULB
    	if(n<m){
    		printf("0
    ");
    		return ;
    	}
    	printf("%lld
    ",fac[n-1]*infac[m-1]%mod*infac[n-m]%mod);
    }
    void work7(){//划分数 UUB 
        if(n<m){
        	printf("0
    ");
        	return ;
    	}
    	f[0][0]=1ll;
    	for(ll i=1ll;i<=n;i++)
    	  for(ll j=1ll;j<=m;j++)
    	    f[i][j]=(f[i-1][j-1]+f[max(i-j,0ll)][j])%mod;
    	printf("%lld",f[n][m]%mod);
    }
    void work8(){//LLA
    	printf("%lld",qpow(m,n));
    }
    void work9(){
    	f[0][0]=1;
    	//前 i个球放在 j个盒子里的方案数 
    	for(ll i=1ll;i<=n;i++)
    	  for(ll j=1ll;j<=m;j++)
    	    f[i][j]=(f[i-1][j-1]+j*f[i-1][j]%mod)%mod;
    	ll ans=0;
    	for(ll i=1;i<=m;i++)
    	  ans=(ans+f[n][i])%mod;
    	printf("%lld",ans);
    }
    void work10(){//ULA
    	ll ans=0;
    	for(ll i=0;i<=m;i++)
    	  ans=(ans+fac[m]*infac[i]%mod*infac[m-i]%mod*fac[n-1]%mod*infac[i-1]%mod*infac[n-i]%mod)%mod;
    	printf("%lld",ans);
    }
    void work11(){//UUA 
    	f[0][0]=1;
    	ll ans=0;
    	for(ll i=1;i<=n;i++)
    	  for(ll j=1;j<=m;j++)
    	    f[i][j]=(f[i-1][j-1]+f[max(0ll,i-j)][j])%mod;
    	for(ll i=1;i<=m;i++) ans=(ans+f[n][i])%mod;
    	printf("%lld",ans);
    }
    int main(){
    	cin>>s;
    	scanf("%lld%lld",&n,&m);
    	if(n<=1000000&&m<=1000000)pre();
    //	cout<<s<<endl;
    //    printf("%lld %lld
    ",n,m);
    	if(s=="LUC") work1();
    	else if(s=="LLC") work2();
    	else if(s=="UUC") work1();
    	else if(s=="ULC") work3();
    	else if(s=="LLB") work4();
    	else if(s=="LUB") work5();
    	else if(s=="ULB") work6();//
    	else if(s=="UUB") work7();
    	else if(s=="LLA") work8();
    	else if(s=="LUA") work9();
    	else if(s=="ULA") work10();//
    	else if(s=="UUA") work11();
    	return 0;
    } 
    
  • 相关阅读:
    C# 灵活切换开发/测试/生成环境web.config
    c# sqlserver 删除大批量数据超时
    vue 上传进度显示
    sqlserver the name is not a valid identifier error in function
    WEBAPI 设置上传文件大小
    vue 获取视频时长
    webapi 导入excel处理数据
    vscode 通过ftp发布vue到azure服务器
    C# 汉字转拼音
    静态代码扫描工具
  • 原文地址:https://www.cnblogs.com/Vimin/p/11777437.html
Copyright © 2020-2023  润新知