难度
3
/
10
3/10
3/10
算清楚,模板没套错(??)的话还是难度不是很高的
题意
有一个集合
S
=
{
1
,
2
,
3
,
⋯
,
n
}
S=\{1,2,3,\cdots,n\}
S={1,2,3,⋯,n}
接下来进行若干次操作,每次操作:
选择一个整数
x
∈
S
x \in S
x∈S,满足
S
S
S 中小于
x
x
x 的元素不超过
m
m
m 个。然后在
S
S
S 中删除
x
x
x。
求出通过以上操作能够得到多少种不同的集合
S
S
S 。
答案取模
998244353
998244353
998244353
样例输入
n
m
n\quad m
nm
3
1
3\quad 1
31
样例输出
7
解释
除了集合 { 1 , 2 } \{1,2\} {1,2} 之外所有原集合的子集都可以得到
数据范围
1
≤
n
≤
1
0
5
1\le n\le 10^5
1≤n≤105
0
≤
m
<
n
0\le m<n
0≤m<n
思路
本体可以正向做也可以反向做。
我这里讲一下反向做的思路。
【按照反向思路考虑的做法】
原集合
S
S
S 的子集数量为
2
n
2^n
2n
现在我们考虑那些非法的集合,并把他们减掉。
什么是非法的方案呢?怎样枚举才能不会有重复的子集呢?
看这个例子,其中
n
=
4
,
m
=
1
n=4,m=1
n=4,m=1
我们从右往左枚举下标
i
i
i.
如果我们对于
i
+
1
∼
n
i+1\sim n
i+1∼n 的所有数都选择,但是
i
i
i 不选择。
这个时候有多少非法方案数呢?
因为我们挖掉了 i , 且非法,所以这个数前面我们至少选择了 m+1个数字。
非法方案数易得为
S
非
法
−
i
=
C
i
−
1
m
+
1
+
C
i
−
1
m
+
2
+
⋯
+
C
i
−
1
i
−
1
S_{非法-i}=C_{i-1}^{m+1}+C_{i-1}^{m+2}+\cdots +C_{i-1}^{i-1}
S非法−i=Ci−1m+1+Ci−1m+2+⋯+Ci−1i−1
那么我们所有非法的方案数为多少呢?
S
非
法
=
∑
n
−
1
i
=
m
+
1
C
i
m
+
1
+
C
i
m
+
2
+
⋯
+
C
i
i
S_{非法}=\underset{i=m+1}{\overset{n-1}{\sum}}C_{i}^{m+1}+C_{i}^{m+2}+\cdots +C_{i}^{i}
S非法=i=m+1∑n−1Cim+1+Cim+2+⋯+Cii
但是这样貌似不好算(可能是我菜呜呜),直接算会是
O
(
N
2
log
N
)
O(N^2\log N)
O(N2logN)的。
(因为算组合数用Lucas定理会带一个log)
我们稍微转换一下可得
【横竖转换】
S 非 法 = ∑ n − 1 i = m + 1 C n − 1 i + C n − 2 i + ⋯ + C i i S_{非法}=\underset{i=m+1}{\overset{n-1}{\sum}}C_{n-1}^{i}+C_{n-2}^{i}+\cdots+C_{i}^{i} S非法=i=m+1∑n−1Cn−1i+Cn−2i+⋯+Cii
接下来我们运用高中学过的组合数学技巧即可求解。
S
非
法
=
∑
n
−
1
i
=
m
+
1
C
i
i
+
C
i
+
1
i
+
⋯
+
C
n
−
1
i
=
∑
n
−
1
i
=
m
+
1
C
i
+
1
i
+
1
+
C
i
+
1
i
+
⋯
+
C
n
−
1
i
=
∑
n
−
1
i
=
m
+
1
C
i
+
1
i
+
1
+
⋯
+
C
n
−
1
i
⋮
=
∑
n
−
1
i
=
m
+
1
C
n
i
+
1
\begin{aligned}S_{非法}&=\underset{i=m+1}{\overset{n-1}{\sum}}C_i^i+C_{i+1}^{i}+\cdots+C_{n-1}^{i}\\ &=\underset{i=m+1}{\overset{n-1}{\sum}}C_{i+1}^{i+1}+C_{i+1}^{i}+\cdots+C_{n-1}^{i}\\ &=\underset{i=m+1}{\overset{n-1}{\sum}}C_{i+1}^{i+1}+\cdots+C_{n-1}^{i}\\ &\quad \vdots\\ &=\underset{i=m+1}{\overset{n-1}{\sum}}C_{n}^{i+1} \end{aligned}
S非法=i=m+1∑n−1Cii+Ci+1i+⋯+Cn−1i=i=m+1∑n−1Ci+1i+1+Ci+1i+⋯+Cn−1i=i=m+1∑n−1Ci+1i+1+⋯+Cn−1i⋮=i=m+1∑n−1Cni+1
这样就可以
O
(
N
log
N
)
O(N\log N)
O(NlogN) 算出来了!
【在思考一下,为什么这样子的非法方案是没有重复计算的?】稍微的YY
对于第
i
i
i 个位置,它
i
+
1
∼
n
i+1\sim n
i+1∼n 的位置都是保留的,数字
i
i
i 是删除的。
前面
1
∼
i
−
1
1\sim i-1
1∼i−1 处长什么样子我们不去管它,只要前面是非法即可。
那么易得,
i
i
i 的位置的所有非法方案都是不同的。
对于
i
≠
j
i\ne j
i=j ,
i
i
i 处的所有非法方案和
j
j
j 处的所有非法方案也都是不同的。
故非法方案没有重复枚举。
核心代码
时间复杂度
O
(
N
log
N
)
O(N\log N)
O(NlogN)
运行时间: 75 ms
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
const int MAX = 10000000;
ll MOD = 998244353 ;
const int INF = 0x3f3f3f3f;
ll qpow(ll a,ll n,ll p){
ll res = 1;
while(n){
if(n&1)res=(res*a)%p;
a=(a*a)%p;
n>>=1;
}
return res%p;
}
ll inv(ll a,ll p){
return qpow(a ,p - 2,p);
}
ll jie[1000005],rjie[1000005];
void init_jie(ll n,ll mod)
{
jie[0]=1;
for(ll i=1;i<=n;i++)jie[i]=jie[i-1]*i%mod;
for(ll i=0;i<=n;i++)rjie[i]=inv(jie[i],mod);
}
ll Lucas(ll n,ll k,ll mod)//返回n取k对mod取模
{
if(n<k)return 0;
if(n>=mod)return Lucas(n/mod,k/mod,mod)*Lucas(n%mod,k%mod,mod)%mod;
else return jie[n]*rjie[n-k]%mod*rjie[k]%mod;
}
int main()
{
int n = 100050;
init_jie(n,MOD);
ll nn,mm;
cin >> nn >> mm;
ll ans = qpow(2,nn,MOD);
for(int i = mm + 2;i<=nn;++i){
ans -= Lucas(nn,i,MOD);
ans %= MOD;
}
ans = (ans + MOD)%MOD;
cout << ans;
return 0;
}