码迷,mamicode.com
首页 > 其他好文 > 详细

组合数学之TwelveFold Way

时间:2019-11-01 16:27:05      阅读:103      评论:0      收藏:0      [点我收藏+]

标签:long   表示   否则   pow   转移   mat   编号   mod   顺序   

组合数学之TwelveFold Way

题目传送


LLC

  • n个球有标号,m个盒子有标号,每个盒子至多放一个球
  • 如果 \(n>m,ans=0\) - 否则为 \(ans=A^n_m=C_m^n\cdot 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_n^m=s_{n-1}^{m-1}+m\cdot 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\n");
    else printf("0\n");
    return ;
}
void work2(){//LLC 
    if(n<=m) printf("%lld",fac[m]*infac[m-n]%mod);
    else  printf("0\n");
//  else if(n==m) printf("")
}
void work3(){
    if(n<=m) printf("%lld",fac[m]*infac[n]%mod*infac[m-n]%mod);
    else printf("0\n");
}
void work4(){//LLB 
    if(n<m){
        printf("0\n");
        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\n",ans);
}
void work5(){//LUB 
    if(n<m){
        printf("0\n");
        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\n",f[n][m]);
}
void work6(){//ULB
    if(n<m){
        printf("0\n");
        return ;
    }
    printf("%lld\n",fac[n-1]*infac[m-1]%mod*infac[n-m]%mod);
}
void work7(){//划分数 UUB 
    if(n<m){
        printf("0\n");
        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",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;
} 

组合数学之TwelveFold Way

标签:long   表示   否则   pow   转移   mat   编号   mod   顺序   

原文地址:https://www.cnblogs.com/Vimin/p/11777437.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!