【牛客挑战赛43:组合数学】B:集合操作

【牛客挑战赛43链接】

难度

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 xS,满足 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 1n105
0 ≤ m < n 0\le m<n 0m<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+1n 的所有数都选择,但是 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} Si=Ci1m+1+Ci1m+2++Ci1i1

那么我们所有非法的方案数为多少呢?
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+1n1Cim+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+1n1Cn1i+Cn2i++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+1n1Cii+Ci+1i++Cn1i=i=m+1n1Ci+1i+1+Ci+1i++Cn1i=i=m+1n1Ci+1i+1++Cn1i=i=m+1n1Cni+1
这样就可以 O ( N log ⁡ N ) O(N\log N) O(NlogN) 算出来了!

【在思考一下,为什么这样子的非法方案是没有重复计算的?】稍微的YY

对于第 i i i 个位置,它 i + 1 ∼ n i+1\sim n i+1n 的位置都是保留的,数字 i i i 是删除的。
前面 1 ∼ i − 1 1\sim i-1 1i1 处长什么样子我们不去管它,只要前面是非法即可。
那么易得, 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值