题意:
定义一个集合S为T的孩子是,对于S中的每一个元素x,在T中都能找到x+1。
给定n,k,每一个集合中的元素x必须满足 1 < = x < = k 1<=x<=k 1<=x<=k且 c n t [ x ] < = 1 cnt[x]<=1 cnt[x]<=1,若n个集合重排后对于 1 < i < = n 1<i<=n 1<i<=n都可以满足 S i − 1 S_{i-1} Si−1为 S i S_i Si的孩子,则该n个集合是一个合法序列,求所有合法序列的个数。
思路:
定义 f [ i ] f[i] f[i]为最后一个集合中若 i i i存在,只看 i i i的贡献(可以构成
合法的之前集合
的总个数)。
则 f [ 1 ] = 1 , f [ i ] = f [ i − 1 ] + 1 f[1]=1,f[i]=f[i-1]+1 f[1]=1,f[i]=f[i−1]+1,因为最后一个集合里,1只能是自己冒出来的不能是由前面变来的,之后每一个数都可以是自己冒出来的(贡献为1),也可以是将使集合中出现i-1
的那个位置上的数提前出现一位,导致原来的i-1变成现在的i。由于最后一个集合中出现每一个数都是独立的,所以可以用乘法原理算得每种情况,求出 p r e [ i ] pre[i] pre[i]表示至少有n-i个空集
的集合有多少情况。
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const ll N = 2e5+50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int fac[N],inv[N];
ll qp(ll a,ll b){
ll ans=1;
a%=mod;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void init(){
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=N;++i){
fac[i]=1ll*fac[i-1]*i%mod; //阶乘
inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; //逆元
}
for(int i=2;i<=N;++i){
inv[i]=1ll*inv[i]*inv[i-1]%mod;
}
}
void work() {
init();
int n,k;cin>>n>>k;
ll ans=0;
for(int i=1;i<=min(n,k);++i){
ll res=(fac[i+1]*qp(i+1,k-i)%mod-fac[i]*qp(i,k-i+1)%mod+mod)%mod;//至少有n-(i+1)个空集-至少有n-i个空集
ans=(ans+res*fac[n]%mod*inv[n-i]%mod)%mod;//恰有n-i个空集的情况
}
ans++;
cout<<ans<<'\n';
}
signed main() {
io;
int t=1;
//cin >> t;
while (t--) {
work();
}
return 0;
}