COMPFEST 15H「组合数学+容斥」

Problem - H - Codeforces

题意:

定义一个集合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 11<i<=n都可以满足 S i − 1 S_{i-1} Si1 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[i1]+1,因为最后一个集合里,1只能是自己冒出来的不能是由前面变来的,之后每一个数都可以是自己冒出来的(贡献为1),也可以是将使集合中出现i-1的那个位置上的数提前出现一位,导致原来的i-1变成现在的i。由于最后一个集合中出现每一个数都是独立的,所以可以用乘法原理算得每种情况,求出 p r e [ i ] pre[i] pre[i]表示至少有n-i个空集的集合有多少情况。

AC代码:
#include 
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;
}

你可能感兴趣的:(Codeforces,算法,c++)