牛客网暑假训练第五场——F take(树状数组求概率期望)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kuronekonano/article/details/81974968

链接:https://www.nowcoder.com/acm/contest/143/F
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
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个盒子,每个盒子出现大小为di的概率为pi,按id顺序遍历盒子,每次遇到比当前钻石大的就换掉,问更换次数期望。

要保证当出现第di个大小的钻石时能更换,那么说明在此之前比其大的钻石都没拿到,那么就是这个钻石之前的比其大的钻石没拿到的概率连乘,用树状数组维护这样失败概率的前缀积,并加上计算每个钻石如果更换的概率,乘上大小,即期望。

树状数组中存储第id个钻石没有di那么大的概率,那么,当在第id个位置更换成di那么大的钻石,就要保证在这之前的钻石都比di小

#include<bits/stdc++.h>
#define LL long long
#define M(a,b) memset(a,b,sizeof a)
#define pb(x) push_back(x)
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1e5+7;
const int p=998244353;
LL qp(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1)ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
struct node
{
    LL val,id,pp;
    bool operator <(const node &a)const
    {
        if(val==a.val)return id<a.id;
        return val>a.val;
    }
}a[maxn];
LL tre[maxn];
int n;
LL inv=qp(100,p-2);///注意题目给出的都是*100之后的概率,因此要*100的逆元才是真正的概率
void update(int pos,LL val)
{
    for(int i=pos;i<=n;i+=lowbit(i))tre[i]=tre[i]*val%p;///树状数组计算前缀乘
}
LL query(int pos)
{
    LL ans=1;
    for(int i=pos;i;i-=lowbit(i))ans=ans*tre[i]%p;
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i].pp,&a[i].val),a[i].id=i;
    sort(a+1,a+1+n);
    fill_n(tre,n,1LL);///初始化全为1
    LL ans=0;
    for(int i=1;i<=n;i++)///按钻石从大到小的顺序更新树状数组,这样每次查询时都是查询该id之前,没有取得 小于自己id,且大小比自己大的钻石的概率
    {
        ans=(ans+query(a[i].id-1)*a[i].pp%p*inv%p)%p;///先查询比自己小的id的比自己大的钻石不取的概率
        update(a[i].id,((LL)100-a[i].pp)*inv%p);///然后再更新取不到该钻石的概率到树状数组中
    }
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/kuronekonano/article/details/81974968