题目链接
题目解法
首先可以考虑将操作逆序
即给定一张全0的数列,有 单点
+
K
+K
+K 与 连续
K
K
K个数
+
1
+1
+1 的操作,且操作完数列和为
M
M
M,求方案数
可以发现对于一段
p
,
p
+
k
−
1
p,p+k-1
p,p+k−1,区间
+
1
+1
+1的操作次数一定
<
K
<K
<K,
且 单点加的集合、区间加的集合 与 方案 形成双射
证明:
- 如果区间 + 1 +1 +1的次数 > = K >=K >=K,那么一定可以转化为单点加 K K K
- 考虑一种方案,通过 a 1 a_1 a1 可以知道 1 − K 1-K 1−K 区间加的次数(即为 a 1 m o d K a_1 \;mod\;K a1modK),以此类推,可以依次通过 a 2 , a 3 , . . . a_2,a_3,... a2,a3,... 知道 2 − K , 3 − K , . . . 2-K,3-K,... 2−K,3−K,... 出现的次数
- 显然,一种方案一定可以对应到一个单点加的集合、区间加的集合
对于
K
∤
M
K\nmid M
K∤M 的情况,答案为0
对于
K
∣
M
K\mid M
K∣M 的情况,即为一个有上限的隔板组合
即有
M
K
\frac{M}{K}
KM 个小球,有
n
n
n 个位置是没有上限的,有
n
−
K
+
1
n-K+1
n−K+1 个位置
<
K
<K
<K,求方案数
对于有限制的组合,一定要想到容斥
容斥至少有
i
i
i 个超上限
所以
a
n
s
=
∑
i
=
0
n
−
k
+
1
(
−
1
)
i
∗
(
i
n
−
k
+
1
)
+
(
2
n
−
k
M
K
−
i
k
+
2
n
−
k
)
ans=\sum_{i=0}^{n-k+1}(-1)^i*(^{n-k+1}_{i})+(^{\frac{M}{K}-ik+2n-k}_{2n-k})
ans=∑i=0n−k+1(−1)i∗(in−k+1)+(2n−kKM−ik+2n−k)
组合数暴力算一算即可
时间复杂度
O
(
n
2
)
O(n^2)
O(n2)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N(4100),P(998244353);
int n,m,k,ans,inv[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int C(int a,int b){
int res=1;
for(int i=0;i<b;i++) res=res*((a-i)%P)%P;
return res*inv[b]%P;
}
int qmi(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%P;
a=a*a%P;
}
return res;
}
signed main(){
n=read(),m=read(),k=read();
if(m%k!=0){ puts("0");exit(0);}
/*
2n-k+1组,有i(0<=i<=n-k+1)个不合法
C(d-i*k+2n-k,2n-k)
*/
int d=m/k;
inv[0]=1;
for(int i=1;i<=2*n;i++) inv[i]=inv[i-1]*qmi(i,P-2)%P;
for(int i=0,j=1;i<=n-k+1;i++,j*=-1){
if(i*k>d) break;
ans=((ans+j*C(n-k+1,i)*C(d-i*k+2*n-k,2*n-k)%P)%P+P)%P;
}
printf("%lld",ans);
return 0;
}