【Nowcoder】暑期多校day5 Take (数学期望 树状数组)

题目大意

有 n 个箱子,每打开一个箱子有 pi 的概率出现一个大小为 di 的钻石。以 1~n 的顺序打开箱子,每开到比手里的钻石更大的钻石就把手里的钻石换掉,求期望交换次数。
https://www.nowcoder.com/acm/contest/143/F


解题思路

基于期望的线性性,分别求出第 i 个箱子的钻石被交换的概率 p = p(第 i 个箱子前面的所有钻石尺寸大于等于第 i 个箱子中钻石尺寸的箱子均未被打开)× p(第 i 个箱子被打开)。

我们以钻石尺寸从大到小为第一关键字,钻石位置从小到大为第二关键字对箱子排序。之后即可用树状数组维护前缀积,也就是 p(第 i 个箱子前面的所有钻石尺寸大于等于第 i 个箱子中钻石尺寸的箱子均未被打开的概率)。

困扰了学长好久,不过想得清晰一点还是比较好写的。


代码

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    register int val=0, sign=1; char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && ch!='-'); ch=='-'?sign=-1:val=ch-'0';
    while(~(ch=getchar()) && (ch>='0' && ch<='9')) val=(val<<1)+(val<<3)+ch-'0';
    return val*sign;
}

#define mp make_pair
#define x first
#define y second
#define lowbit(k) ((k)&(-(k)))
const int maxn=int(1e5)+111, moder=998244353, inv100=828542813;
typedef pair<int,int> pii;
inline int mul(const int &a,const int &b) {return 1ll*a*b%moder;}
inline int add(const int &a,const int &b) {return (a+b<moder)?a+b:a+b-moder;}
bool cmp(const pii &a,const pii &b) {if(a.x==b.x) return a.y<b.y; else return a.x>b.x;}

int n;
int p[maxn], d[maxn];
pii cp[maxn];
int pro[maxn];

void modify(int pos,int v) {
    for(;pos<=n;pro[pos]=mul(pro[pos],v),pos+=lowbit(pos));
    return;
}
int query(int pos) {
    int res=1;
    for(;pos;res=mul(res,pro[pos]),pos-=lowbit(pos));
    return res;
}

void work() {
    n=read();
    register int i;
    for(i=1;i<=n;++i) {
        p[i]=read(), d[i]=read();
        pro[i]=1;
        cp[i]=mp(d[i],i);
    }

    sort(cp+1,cp+1+n,cmp);
    int ans=0;
    for(i=1;i<=n;++i) {
        int id=cp[i].y, val=query(id);
        ans=add(ans,mul(val,mul(p[id],inv100)));
        modify(id,mul(100-p[id],inv100));
    }
    printf("%d\n",ans);
    return;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
#endif
    work();

    return 0;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页