题目:
连续n个物品,总共选了m个,选中的物品连续分布最长为k个的方案数
算法:
组合计数+容斥
分析:
在计算连续分布最长为k个的方案数之前,我们先考虑n个物品,总共选m个,选中的物品连续分布最长大于等于k的方案数。
我们记F[k]为个物品,总共选m个,选中的物品连续分布最长大于等于k的方案数,显然题目要求的方案数就是F[k]-F[k+1]。
接下来求F[k],总共有 n-m 个物品没有被选上,我们把这 n-m 个物品看作隔板,用插空法插入选中的物品,因为至少要选一段连续的大于等于k个,我们先枚举连续大于等于k个的选了多少段,再计算连续选的大于等于k个在哪几个空,这样计算显然会算重复,因此需要容斥。
我们计i为连续大于等于k个的段数,n-m 个隔板隔出了 n-m+1 个空,连续大于等于k个的段数插在哪些空的方案数为C(n-m+1, i),剩下的选的物品可以任意放,方案数为C(n-i*k, n-m),即在除连续大于等于k个物品之外剩下的物品 n-i*k 个中,选出 n-m 个当隔板。
最后答案即为
代码:
const int N = 1e6+10;
const int MOD = 998244353;
int fac[N],ifac[N];
int qmi(int a,int b,int p){
int res=1;
while(b){
if(b&1)res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
int get_inv(int x){
return qmi(x,MOD-2,MOD);
}
void init(){
fac[0]=1;
for(int i=1;i<N;i++)fac[i]=fac[i-1]*i%MOD;
ifac[N-1]=1*get_inv(fac[N-1]);
for(int i=N-1;i>0;i--)ifac[i-1]=ifac[i]*i%MOD;
}
int C(int n, int m){
if(m>n)return 0;
if(m==0)return 1;
return (fac[n]*ifac[n-m]%MOD)*ifac[m]%MOD;
}
int F(int n,int m,int k){
int res=0,st=1;
for(int i=1;i*k<=m&&i<=n-m+1;i++){
if(st)res=(res+C(n-m+1,i)*C(n-i*k,n-m)%MOD)%MOD;
else res=(res-C(n-m+1,i)*C(n-i*k,n-m)%MOD+MOD)%MOD;
st^=1;
}
return res;
}
void solve(){
int n,m,k;
cin>>n>>m>>k;
init();
cout<<(F(n,m,k)-F(n,m,k+1)+MOD)%MOD<<endl;
}