牛客周赛 94 F 小苯的小球分组

F.小苯的小球分组


原题链接

题意简述

\(\hspace{15pt}\)小苯有 \(n\) 个小球,其中第 \(i\) 个小球的颜色为 \(a_i\)。从其中取出一些小球,组成一个小球集合 \(\Bbb S\)。定义小球集合 \(\Bbb S\) 的函数 \(f(\Bbb S)\),表示将小球集合 \(\Bbb S\) 分为若干组,满足以下所有条件的最少分组个数:
\(\hspace{23pt}\bullet\) 每组最多有 \(2\) 个球;
\(\hspace{23pt}\bullet\) 组内有 \(2\) 个球的组,这 \(2\) 个球的颜色不同。
\(\hspace{15pt}\)现在小苯希望你求出这 \(n\) 个小球的所有非空子集(按位置区分)所对应的 \(f\) 函数值之和。由于答案可能很大,请将答案对 \(998\,244\,353\) 取模后输出。

解题思路

先单独考虑一个集合中分组个数的可能性,发现根据区间绝对众数的性质,有分组个数一定是$ max(n/2,cnt_{max})$,即一定是区间元素个数除以 \(2\) 向上取整或者 区间出现次数最多的数的出现次数。不妨假设原先所有集合对原序列的贡献是 \([n/2]\) ,在考虑哪些集合可能被误算了,消去误算集合的影响后得到的一定是答案。如果原序列所有集合的贡献都是 \([n/2]\) 考虑枚举集合长度,那贡献的答案就是 $ \sum_{i=1}^{len} C(len,i)·[n/2]$,在考虑统计第二种贡献的影响, $\sum_{i=1} ^{n} \sum_{j=1}^{sum} \sum _{k=0}^{min(j,sum-cnt_i)}C(cnt_i,j)·C(sum-cnt_i,k)·j $ ,表示在第二种贡献是先在原序列中选取一种颜色,然后选取这种颜色出现次数,然后选取其他元素出现次数,最后将这些枚举值相乘即是答案

AC code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const ll inf=1e18;
const ll mod=998244353;
struct Comb{
    vector<ll>fac,inv_fac;
    Comb(int n){ init(n);}
    ll q_pow(ll a,ll b){
        ll s=1;
        while(b){
            if(b&1){
                s=s*a%mod;
            }
            a=a*a%mod;
            b>>=1;
        }
        return s;
    }
    void init(int n){
        fac.resize(n+1);
        inv_fac.resize(n+1);
        fac[0]=1;
        for(int i=1;i<=n;i++) fac[i]=i*fac[i-1]%mod;
        inv_fac[n]=q_pow(fac[n],mod-2);
        for(int i=n-1;i>=0;i--) inv_fac[i]=(i+1)*inv_fac[i+1]%mod;
    }
    ll C(ll x,ll y){
        return fac[x]*inv_fac[x-y]%mod*inv_fac[y]%mod;
    }
    ll A(ll x,ll y){
        return fac[x]*inv_fac[y]%mod;
    }
}comb(5005);//组合数板子
void solve(){
    int n;cin>>n;
    vector<ll>a(n+1);
    map<ll,ll>mp;
    for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++;
    ll ans=0;
    for(int i=1;i<=n;i++){
        ans=(ans+(i+1LL)/2*comb.C(n,i)%mod)%mod;
    }//假设众数来源全是第一种
    sort(a.begin()+1,a.end());
    ll sum=n;
    a.erase(unique(a.begin()+1,a.end()),a.end());
    n=a.size()-1LL;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=mp[a[i]];j++){
            for(int k=0;k<=min(j-1LL,sum-mp[a[i]]);k++){
                ll del=(k+j+1LL)/2*comb.C(mp[a[i]],j)%mod*comb.C(sum-mp[a[i]],k)%mod;
                ll add=j*comb.C(mp[a[i]],j)%mod*comb.C(sum-mp[a[i]],k)%mod;
                ans=(ans-del+mod)%mod;
                ans=(ans+add)%mod;
            }
        }
    }//尝试累加第二种并消去第一种的影响
    cout<<ans<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int T=1;cin>>T;
    while(T--) solve();
    return 0;
}
posted @ 2025-05-31 19:13  usedchang  阅读(8)  评论(0)    收藏  举报