本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2023-02-22 23:05:26
题目大意: 给定一个集合,给出k次操作,每次可以取集合的一个子集,找出子集中最小的没有出现过的非负整数,记录着k个整数。问最后这k个整数有多少种不同可能的集合。
\/*
先手工做几个样例
样例1:
3 2
0 1 2
- -
0 0
0 1
1 1
0 2
1 2
2 2
0 3
1 3
2 3
3 3
3 4
样例2:
3 3
2 4 6
- - -
0 0 0
0 0 1
0 1 1
0 1 2
0 1 3
相当与非降序的填入数字。(每个数据有可填入的最早位置)
数据非降序填入,类似P6475 [NOI Online #2 入门组] 建设城市
\/\/集合问题,不考虑顺序,以从小到大为默认顺序。转换为多重集组合问题
回顾下隔板法解决多重集组合问题。
把8个球放入3个盒子,不允许空盒
oo|oo|oooo,放入2个分割线||
x1+x2+x3=8
00 11 2222 xi>0 (2 2 4)
ooo|oo|ooo (3,2,3)
000 11 222
把8个球放入3个盒子,允许空盒
x1+x2+x3=8 (0,0,8)
(x1+1)+(x2+1)+(x3+1)=11 (1 1 9)
*|*|*oooooooo
0 1 222,222,222
每个盒子删掉1个球后实际数据为22,222,222
可填入的数据为[0,n+cnt] cnt为可以补入的数据个数
设最后一个位置上可以填写的数为i,要想填入i,前面0-(i-1)必需都在集合内出现过。
如果有数没有出现,那么需要再前面补上。可以用桶统计出需要补的个数m,从总数k中扣掉需要补的位置。
那么剩下的位置就可以填写0-i之间的数值了共有(i+1)个数,可以重复填写再k-m-1个位置上,允许空盒,多重集组合。
*\/
#include <bits\/stdc++.h>
using namespace std;
const int N=4e5+90;
typedef long long int ll;
const ll inf = 1e18, mod = 998244353;
ll f[N]={1,1},invf[N]={1,1},inv[N]={1,1};
ll a[N];
ll C(ll n,ll r){
if(n<r||n<0)return 0;
return f[n]*invf[r]%mod*invf[n-r]%mod;\/\/c(n,r)=n!\/(n-r)!\/r!;
}
ll work(){
ll n, k, ans = 0,sum=0;
cin >> n >> k;
for (ll i = 0, x; i < n; ++i) {
cin >> x; a[x] = 1;
}
for (ll i = 0; i < n + k; ++i) {
if (sum > k) break;\/\/能填写的最大数
ll m= k-sum-1;\/\/前面需要留出补k-sum个位置补数,后面第k个需要写i,可填写的位置为k-sum-1个
ll r=i+1;\/\/可填写的数据为0..i,多重集个数i+1 相当于把m个球放入r个盒子,允许空盒。
ans = (ans + C(m+ r-1, r-1)) % mod;
sum+=1-a[i];
}
return ans;
}
int main() {
\/\/ freopen("out.txt","w",stdout);
for (ll i = 2; i <N; ++i) {
f[i] = f[i - 1] * i % mod;
inv[i] = (mod - (mod \/ i * inv[mod % i] % mod)) % mod;
invf[i] = inv[i] * invf[i-1] % mod;
}
cout << work() << "\n";
return 0;
}

鲁ICP备2025150228号