2018 牛客多校五 F take(线段树)

https://www.nowcoder.com/acm/contest/143/F

题意:一个人依次按顺序打开1-n号宝箱。若宝箱里有一颗比他手上还大的钻石,他会换走。给你宝箱i里Di大的钻石出现的几率是Pi,问你交换的期望。

思路:总期望就是每个宝箱被交换期望之和,因为只有箱子里的钻石比手中的钻石大才会被交换,如果假设我们手中的钻石是最后的钻石的话,那么比后面箱子里比他手中大的钻石都是不可能出现的。我们就可以给他排个序,然后用线段树,从1到这个这个序号的宝箱比他大的钻石的不可能开出概率乘积,然后再更新这个箱子的不可能开出的概率。因为这里的概率是乘了100的,后面要除100,所以需要用到逆元,100的逆元为828542813。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+5;
const int mod=998244353;
int n;
struct node
{
    int x,y,id;
}a[maxn<<2];
ll sum[maxn<<2];
bool cmp(node a,node b)
{
    if(a.y!=b.y)
    return a.y>b.y;
    return a.id<b.id;
}
void update(int l,int r,int k,int num,int x)
{
    if(l==r)
    {
        sum[x]=(long long)(100-num)*828542813%mod;
        return;
    }
    int mid=(l+r)/2;
    if(k<=mid)
        update(l,mid,k,num,x*2);
    else update(mid+1,r,k,num,x*2+1);
    sum[x]=sum[x*2]*sum[x*2+1]%mod;
}
ll query(int l,int r,int L,int R,int x)
{
    if(L<=l&&R>=r)
    {
        return sum[x];
    }
    int mid=(l+r)/2;
    ll ans=1;
    if(l<=mid)ans=ans*query(l,mid,L,R,x*2)%mod;
    if(R>mid)ans=ans*query(mid+1,r,L,R,x*2+1)%mod;
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        a[i].id=i;
    }
    sort(a+1,a+1+n,cmp);
    for(int i=0;i<=n<<2;i++)
    {
        sum[i]=1LL;
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=(ans+query(1,n,1,a[i].id,1)*a[i].x%mod*828542813)%mod;
        update(1,n,a[i].id,a[i].x,1);
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/imzxww/article/details/81412683