2024.04.06 组合计数
2024/4/6 组合计数
CF128C - Games with Rectangle
给定一个 \(n\times m\) 的网格图,每次在当前矩形严格内部划定一个子矩形,划定 \(k\) 次,有多少种方案。
唯一性太明显了,显然答案就是
\[\binom{n-1}{2k}\binom{m-1}{2k}
\]
#include <bits/stdc++.h>
using namespace std;
#define ld long double
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return fl?-x:x;
}
#define read() read<int>()
const int maxn = 1e3 + 10;
const int P = 1e9 + 7;
int fac[maxn],invfac[maxn],n,m,k;
int power(int a,int b){
int res=1;
while(b){
if(b&1)res=1LL*a*res%P;
a=1LL*a*a%P;
b>>=1;
}
return res;
}
inline int C(int n,int m){
if(m>n)return 0;
return 1LL*fac[n]*invfac[m]%P*invfac[n-m]%P;
}
int main(){
n=read();m=read();k=read();
fac[0]=1;
for(int i=1;i<=1000;i++)fac[i]=1LL*fac[i-1]*i%P;
invfac[1000]=power(fac[1000],P-2);
for(int i=1000;i>=1;i--)invfac[i-1]=1LL*invfac[i]*i%P;
cout<<1LL*C(n-1,2*k)*C(m-1,2*k)%P<<endl;
return 0;
}
CF111D - Petya and Coloring
给定一个 \(n\times m\) 的网格图,用 \(k\) 种颜色来对所有的格子染色。要求:
对于沿格子的线穿过的任何垂直线,会将棋盘分成两个非空的部分,这两个部分中的不同颜色的数量应相同。
\(n,m\leq 1000\)
题目条件等价为:随直线左右移动,左右两个颜色集合不变。
则中间 \(m-2\) 列元素只能染交集的颜色。
因此枚举第一列和最后一列的颜色交集为 \(i\) 种,第一列和最后一列颜色种数为 \(j\) 种 \((j\geq i)\)。
则答案为:
\[\sum_{i=0}^{n}\sum_{j=i}^{n}\binom{k}{i}\binom{k-i}{j-i}\binom{k-j}{j-i}(f_{n,j}\times j\ !)^2\times i^{n(m-2)}
\]
#include <bits/stdc++.h>
using namespace std;
#define ld long double
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return fl?-x:x;
}
#define read() read<int>()
const int maxn = 1e6 + 10;
const int maxm = 1e3 + 10;
const int P = 1e9 + 7;
inline void add(int &x,int y){
x+=y;
if(x>=P)x-=P;
}
int fac[maxn],invfac[maxn],n,m,k;
int f[maxm][maxm];
int power(int a,int b){
int res=1;
while(b){
if(b&1)res=1LL*a*res%P;
a=1LL*a*a%P;
b>>=1;
}
return res;
}
inline int C(int n,int m){
if(n<0 || m<0)return 0;
if(m>n)return 0;
return 1LL*fac[n]*invfac[m]%P*invfac[n-m]%P;
}
inline void init(){
fac[0]=1;
for(int i=1;i<=1000000;i++)fac[i]=1LL*fac[i-1]*i%P;
invfac[1000000]=power(fac[1000000],P-2);
for(int i=1000000;i>=1;i--)invfac[i-1]=1LL*invfac[i]*i%P;
}
int main(){
n=read();m=read();k=read();
init();
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)add(f[i][j],f[i-1][j-1]),add(f[i][j],1LL*j*f[i-1][j]%P);
}
if(m==1){
printf("%d\n",power(k,n));return 0;
}
int ans=0;
for(int i=0;i<=n;i++){
for(int j=i;j<=n;j++){
int tmp1=1LL*C(k,i)*C(k-i,j-i)%P*C(k-j,j-i)%P;
int tmp2=1LL*f[n][j]*fac[j]%P;tmp2=1LL*tmp2*tmp2%P;
int tmp3=power(i,n*(m-2));
add(ans,1LL*tmp1*tmp2%P*tmp3%P);
}
}
printf("%d\n",ans);
return 0;
}
CF886E. Maximum Element
计算题目所需的在中途跳出函数的排列数量容易算重,考虑补集转化。
令 \(f_i\) 表示长度为 \(i\) 的排列,不中途跳出函数的排列数量。
考虑最大的数 \(i\),\(i\) 的位置 \(j\in[i-k+1,i]\),因此枚举 \(j\) 进行统计。
\[f[i]=\sum_{j=i-k+1}^{i}\binom{i-1}{j-1}\times f[j-1] \times (i-j)!= (i-1)!\sum_{j=i-k+1}^{i}\frac{f_{j-1}}{(j-1)!}
\]
则长度为 \(n\) 的所有答案为 \(n\) 的排列数为:
\[ans=\sum_{i=1}^{n}\binom{n-1}{i-1}\times f[i-1] \times (n-i)!
\]
#include <bits/stdc++.h>
using namespace std;
#define ld long double
template <typename T>
inline T read(){
T x=0;char ch=getchar();bool fl=false;
while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return fl?-x:x;
}
#define read() read<int>()
const int maxn = 1e6 + 10;
const int P = 1e9 + 7;
inline void add(int &x,int y){
x+=y;
if(x>=P)x-=P;
}
int power(int a,int b){
int res=1;
while(b){
if(b&1)res=1LL*res*a%P;
a=1LL*a*a%P;
b>>=1;
}
return res;
}
int fac[maxn],invfac[maxn],n,k;
int f[maxn],s[maxn];
inline int C(int n,int m){
if(n<0 || m<0 || n<m)return 0;
return 1LL*fac[n]*invfac[n-m]%P*invfac[m]%P;
}
int main(){
n=read();k=read();
fac[0]=1;
for(int i=1;i<=1000000;i++)fac[i]=1LL*fac[i-1]*i%P;
invfac[1000000]=power(fac[1000000],P-2);
for(int i=1000000;i>=1;i--)invfac[i-1]=1LL*invfac[i]*i%P;
f[0]=1;s[0]=1;
for(int i=1;i<=n;i++){
int Lj=max(0,i-k),Rj=i-1;
int tmp;
if(!Lj)tmp=s[Rj];
else tmp=(s[Rj]-s[Lj-1]+P)%P;
add(f[i],1LL*fac[i-1]*tmp%P);
add(s[i],1LL*f[i]*invfac[i]%P);
add(s[i],s[i-1]);
}
int ans=0;
for(int i=1;i<=n;i++){
add(ans,1LL*C(n-1,i-1)*f[i-1]%P*fac[n-i]%P);
}
ans=(fac[n]-ans+P)%P;
cout<<ans<<endl;
return 0;
}