[2018牛客多校(第五场)] take (树状数组+概率)

题目链接

take

题目大意

一共有n个箱子, 第 i i 个箱子有p[i]%的概率开出大小为 d[i] d [ i ] 的钻石。 初始玩家手里有一个size为0的钻石, 且手里最多只能有一个钻石, 如果当前开的箱子中得到的钻石大于手中的钻石, 则替换, 问最终替换次数的期望是多少。 最终对答案模998244353。

数据范围

1n105 1 ≤ n ≤ 10 5
1p[i]100 1 ≤ p [ i ] ≤ 100
1d[i]109 1 ≤ d [ i ] ≤ 10 9

解题思路

题意是让求交换钻石次数的期望, 因为根据期望的性质 E(x+y)=E(x)+E(y) E ( x + y ) = E ( x ) + E ( y ) , 可以将问题转换为每个钻石对次数期望的贡献, 因为每个钻石最多只能交换一次, 所以每个钻石的贡献就是这个钻石被选中的概率 × × 这个钻石能贡献的交换次数(1次), 也就是求每个钻石被选中的概率和。
如果第 i i 个钻石要被选中, 那么前i1 size s i z e 比他大的都不能出现, 其他小的无所谓, 所以对于第 i i 个钻石对答案的贡献就是p[i]×(j<i,d[j]>d[i])(100p[j]).

所以每次需要求一个前缀乘积, 可以用树状数组或者线段树解决。
因为是维护乘积, 所以要将树状数组初始化为1, 同时还需要考虑维护分数用逆元处理一下。

使用树状数组按照size大小从大到小排序, 那么每次查询的树状数组的前缀乘积都是比他大的数的乘积。

/********************************************
 *Author*        :ZZZZone
 *Created Time*  : 日  8/ 5 17:08:31 2018
 * Ended  Time*  : 日  8/ 5 17:28:23 2018
*********************************************/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
#define debug(x) std::cerr << #x << " = " << (x) << std::endl
typedef pair<int, int> PII;
typedef long long LL;
typedef unsigned long long ULL;

inline void OPEN(string s){
    freopen((s + ".in").c_str(), "r", stdin);
    freopen((s + ".out").c_str(), "w", stdout);
}

const int MAXN = 1e5;
const int Mod = 998244353;

struct Node{
    int p, sz, id;
    bool operator < (const Node& tmp) const{
        return sz > tmp.sz || (sz == tmp.sz && id < tmp.id);
    }
}a[MAXN+5];

LL bitsum[MAXN+5];
int n;

LL fast_pow(LL x, LL y){
    LL res = 1LL;
    while(y != 0){
        if(y & 1) res =(res * x) % Mod;
        y >>= 1;
        x = (x * x) % Mod;
    }
    return res;
}

inline int lowbit(int x){return (x & (-x)); }

inline void Add(int p, LL x){
    while(p <= n){
        bitsum[p] = (bitsum[p] * x) % Mod;
        p += lowbit(p);
    }
}

inline LL Sigma(LL p){
    LL res = 1LL;
    while(p){
        res = (res * bitsum[p]) % Mod;
        p -= lowbit(p);
    }
    return res;
}

void Init(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d %d", &a[i].p, &a[i].sz);
        a[i].id = i;
    }
    sort(a + 1, a + 1 + n);
    for(int i = 0; i <= MAXN; i++) bitsum[i] = 1LL;
}

void Solve(){
    LL ans = 0LL ,inv = fast_pow(100LL, Mod - 2);
    for(int i = 1; i <= n; i++){
        LL tmp = Sigma(a[i].id-1) * (LL)a[i].p % Mod * inv % Mod;
        ans = (ans + tmp) % Mod;
        Add(a[i].id, inv * (100LL - (LL)a[i].p) % Mod);
    }
    printf("%lld\n", ans);
}

int main()
{
    Init();
    Solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值