AtCoder Beginner Contest 215 G - Colorful Candies 2

AtCoder Beginner Contest 215 G - Colorful Candies 2

有n个糖果,每个糖果有着一个颜色a[i],每次拿k个糖果期望拿到E(x)个不同颜色的糖果,求出k从1~n分别得到的E(x)。最终答案mod998244353。
数据范围n <= 5e4

看到这个问题首先反应是答案和颜色的具体值以及出现的顺序无关,所以离散化之后用一个cnt数组记录他们分别的出现次数之后就可以不用去管a[i]了。

之后可以发现,要求出选k个糖果后不同颜色的糖果的期望值,就是求出每一种颜色的糖果至少出现一次的概率,然后求和。P(至少出现一次)=1-P(一次都不出现),而一次都不出现的概率,假如当前这个颜色的糖果有x个,那么就是 C n − x k / C n k C_{n-x}^{k}/C_n^k Cnxk/Cnk

如果就这样暴力求解的话,最终的复杂度最坏为 O ( n 2 ) O(n^2) O(n2)的,不过可以想到很明显的优化,一个是当k>n-x时一次都不出现的概率为0,所以可以将cnt的值进行排序然后从小到大枚举,一旦达到k>n-cnt[i]的条件就可以直接跳出循环。另一个就是相同是cnt进行去重,求出的答案直接乘以相同cnt的数量,由于 ∑ c n t = n \sum cnt=n cnt=n,当满足cnt直接各不相同时,cnt个数为 O ( n ) O(\sqrt n) O(n )。这样总复杂度就是 O ( n n ) O(n\sqrt n) O(nn ),时限给4秒很宽松。

#include <bits/stdc++.h>
#define f(i, l, r) for(int i = l; i <= r; i ++)
#define nf(i, r, l) for(int i = r; i >= l; i --)
typedef long long ll;
using namespace std;

const int N = 5e4 + 10, mod = 998244353;
int a[N], cnt[N], tot, ccnt[N];
long long jc[N], ny[N];

long long C(int n, int m, int num)
{
    if (m == 0 || m == n)
        return 1;
    long long res = 1;
    if (!num)
    {
        res = jc[n] * ny[m];
        res %= mod;
        res *= ny[n - m];
        res %= mod;
    }
    else
    {
        res = jc[m] * jc[n - m];
        res %= mod;
        res *= ny[n];
        res %= mod;
    }
    return res;
}

long long qpow(long long x, int y)
{
    long long res = 1;
    while (y)
    {
        if (y & 1)
        {
            res *= x;
            res %= mod;
        }
        x *= x;
        x %= mod;
        y >>= 1;
    }
    return res;
}

void init(int n)
{
    jc[0] = 1;
    f(i, 1, n)
    {
        jc[i] = jc[i - 1] * i;
        jc[i] %= mod;
    }
    f(i, 1, n)
        ny[i] = qpow(jc[i], mod - 2);
}

int main()
{
    #ifdef jinxes6
    freopen("in.txt", "r", stdin);
    #endif

    ios::sync_with_stdio(false);
    cin.tie(0);

    int T = 1;
    //cin >> T;
    while (T --)
    {
        int n;
        cin >> n;
        init(n);

        f(i, 1, n)
            cin >> a[i];
        sort (a + 1, a + n + 1);

        f(i, 1, n)
        {
            if (a[i] > a[i - 1])
                cnt[++ tot] = 1;
            else
                cnt[tot] ++;
        }
        sort (cnt + 1, cnt + tot + 1);
        int p = 1, t = tot;
        f(i, 1, tot)
        {
            if (cnt[p] != cnt[i]) 
            {
                ccnt[++ p] = 1;
                cnt[p] = cnt[i];
            }
            else
                ccnt[p] ++;
        }
        tot = p;

        f(i, 1, n)
        {
            long long ans = t;
            f(j, 1, tot)
            {
                if (i > n - cnt[j])
                    break;
                long long temp = C(n - cnt[j], i, 0) * C(n, i, 1) % mod;
                temp = temp * ccnt[j] % mod;
                ans = (ans - temp + mod) % mod;
            }
            cout << ans << "\n";
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值