2018牛客暑假多校第五场 F(树状数组)

题目描述:

    有 n 个箱子,第 i 个箱子有 p[i] 的概率出现大小为 d[i] 的钻石。现在 小A 一开始手里有一个大小为 0 的钻石,他会根据 i 从小到大打开箱子,如果箱子里有钻石且比小 A 手中的大,那么小 A 就会交换手中的钻石和箱子里的钻石求期望的交换次数
1<=n<=10^5

题目分析:

    根据我们的概率统计基础,如果出现的钻石的大小顺序是升序的话,那么题目的答案根据概率计数的加法原理不难求出为 。但是,倘若在某一位处出现了当前钻石大小d[i]>dmaxn的情况,如果我们要选择这个箱子的钻石,只能希望从前面中的第一个大于d[i]的第j位开始到第i位,都不能选其中的钻石,故此时根据概率奇数的乘法原理,不难起初对于这一位而言,当前值对答案的贡献为,其中p[a]为第a位选中的概率,p[i]为当前位的概率。更一般的,对于任意一位数,当前位对答案的的贡献为这个东西取的概率乘以前面比它大的东西不取的概率。即

    因此现在我们所需要处理的就是一个前缀积。

    故我们可以将下标id根据d的大小进行排序,用树状数组维护1 - pi的前缀积即可。时间复杂度O(nlogn).

    ps:值得注意的是,因为这个题目给出来的概率为p[i]*100,而答案要我们统计的是期望,因此我们还需要求出100的逆元,每一次的求解中都乘上inv(100)才行。

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;
const int mod=998244353;
ll bit[maxn];
int n,cnt;
//求解逆元
ll powmod(ll a,ll n){
    ll res=1;
    while(n){
        if(n&1) res=res*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return res;
}
ll inv(ll a){
    return powmod(a,mod-2)%mod;
}//BIT
ll lowbit(ll x){
    return x&-x;
}
void update(ll x,ll num){
    while(x<=maxn){
        bit[x]=bit[x]*num%mod;
        x+=lowbit(x);
    }
}
ll query(int x){
    ll ans=1;
    while(x>0){
        ans=ans*bit[x]%mod;
        x-=lowbit(x);
    }
    return ans;
}
void init(){
    for(int i=0;i<=maxn;i++){
        bit[i]=1;
    }
}
ll p[maxn],d[maxn],dd[maxn];
int id[maxn];
bool cmp(int x,int y){//将下标id按照d的降序大小排序
    if(d[x]!=d[y]) return d[x]>d[y];
    else return x<y;
}
int main()
{
    init();
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%lld",&p[i],&d[i]);
        int tmp=p[i];
        id[i]=i;
        p[i]=(ll)(100-p[i])*inv(100)%mod;//先预处理出乘上100的逆元的数
        dd[i]=(ll)tmp*inv(100)%mod;
    }
    sort(id+1,id+1+n,cmp);
    ll ans=0;
    for(int i=1;i<=n;i++){//统计答案
        int pos=id[i];
        ans+=query(pos)*dd[pos];
        ans%=mod;
        update(pos,p[pos]);
    }
    cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_39453270/article/details/81368447