牛客网暑期ACM多校训练营(第五场)F(take)

本文介绍了一种计算在特定条件下钻石替换次数期望值的算法。通过将问题转化为概率计算问题,利用树状数组维护前缀积的方法实现了高效求解。文章详细解释了算法思路,并给出了完整的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述 
Kanade has n boxes , the i-th box has p[i] probability to have an diamond of d[i] size.

At the beginning , Kanade has a diamond of 0 size. She will open the boxes from 1-st to n-th. When she open a box,if there is a diamond in it and it's bigger than the diamond of her , she will replace it with her diamond.

Now you need to calculate the expect number of replacements.

You only need to output the answer module 998244353.

Notice: If x%998244353=y*d %998244353 ,then we denote that x/y%998244353 =d%998244353

输入描述:
The first line has one integer n.

Then there are n lines. each line has two integers p[i]*100 and d[i].
输出描述:
Output the answer module 998244353
示例1
输入

3
50 1
50 2
50 3
输出

499122178
备注:
1<= n <= 100000

1<=p[i]*100 <=100

1<=d[i]<=10^9

题意:有n个箱子, 每个箱子里面有p[i]/100的概率有一个大小为d[i]的钻石,一开始你手上的钻石大小为0, 你从第一个箱子开始, 依次打开每一个箱子, 如果箱子里面的钻石大小比你手上的大, 那就拿起箱子里的钻石替换自己的, 求最后替换次数的期望。

思路:来自:https://blog.youkuaiyun.com/xiuya19/article/details/81380266

如果直接枚举 交换次数*概率 不仅机器跑步过去,自己的脑子也跑不过去(比赛时自己出的样例算出了3个结果)
实际上,如果我们规定某一颗钻石K是最后的手持的话,那么比它大的钻石盒子就不能开出钻石,
那么显然钻石K对答案的贡献就是打开K的盒子有钻石的概率和打开比他大的盒子但是没钻石的概率。
这样以来实际上顺序开对这种计算方式是没影响的。
这样的话我们对钻石大小从大到小排序,枚举每一颗钻石得到贡献,其和就是答案。
这里可以用树状数组维护前缀积能快速得到比当前钻石大的钻石开不出来的概率乘积。
时间复杂度O(nlogn)
另外有一点要注意的是,因为题目给出的概率是p*100,并且最终结果要取模,因此推荐p用整型保存,计算概率的时候用p*(100的逆元)。
还有就是因为树状数组维护的是前缀积,因此初始化都要变为1.

代码:

#include <bits/stdc++.h>

using namespace std;
#define ll long long
const int maxn=1e5+10;
const int mod=998244353;
const int INF=0x3f3f3f3f;
struct point
{
    ll p,d,id;
}G[maxn];

long long pow_mod(long long a,long long b,long long m)
{
    a=a%m;
    long long ans=1;
    while(b)
    {
        if(b&1)
        {
            ans=(ans*a)%m;
            b--;
        }
        b>>=1;
        a=a*a%m;
    }
    return ans;
}
bool cmp(const point &a,const point &b)
{
    if(a.d>b.d)return 1;
    else if(a.d==b.d)
    {
        return a.id<b.id;
    }
    return 0;
}
ll c[maxn];
ll lowbit(ll x)
{
    return x&(-x);
}
void add(ll x,ll v)
{
    while(x<maxn)
    {
        c[x]=(c[x]*v)%mod;
        x+=lowbit(x);
    }
}
ll Sum(int x)
{
    ll sum=1;
    while(x>0)
    {
        sum=(sum*c[x])%mod;
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    int n;
    scanf("%d",&n);
    ll inv=pow_mod(100,mod-2,mod);
    for(int i=1;i<=maxn;i++)c[i]=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&G[i].p,&G[i].d);
        G[i].id=i;
    }
    sort(G+1,G+1+n,cmp);
    ll sum=0;
    for(int i=1;i<=n;i++)
    {
        ll id=G[i].id;
        ll p=G[i].p;
        ll sum_1=(Sum(id-1)*((inv*p)%mod))%mod;
        sum=(sum+sum_1)%mod;
        add(id,inv*(100-p)%mod);
    }
    printf("%lld\n",sum);
    return 0;
}
/*
3
50 1
50 2
50 3
*/
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值