牛客多校第五场 F - take (树状数组)

题目链接

初始的时候手里有大小为0的钻石,现在依次打开n个箱子,第i个箱子有x/100的概率开出大小为y的钻石。如果开出的钻石比手中的要大,就交换一次。求交换次数的期望。

考虑要在某个位置交换,就必须满足在它前面的比它大的钻石都没有被开出来。

也就是,第i个位置对答案的贡献是p_{i}*\prod _{j<i,d_{j}>d_{i}}(1-p_{j})

对于这个东西,我们可以将d从大到小排序,用树状数组去维护(1-pi)的前缀积。依次加入到树状数组中时,由于加入的肯定是比当前的钻石更大的,所以只需要查询一波前缀积就可以更新答案。

相应的,考虑到d的范围,需要进行离散化。

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn = 100050;
const ll INF = (1LL << 62) - 1;
const ll mod = 998244353;

int n;
ll tree[maxn], d[maxn];
int lowbit(int x) {return x & (-x);}
struct node
{
    ll p, d;
}e[maxn];
ll qpow(ll a, ll x)
{
    ll res = 1;
    while(x)
    {
        if(x & 1) res = (res*a) % mod;
        a = (a*a) % mod;
        x >>= 1;
    }
    return res;
}

ll query(int x)
{
    ll res = 1;
    while(x <= n)
    {
        res = (res*tree[x]) % mod;
        x += lowbit(x);
    }
    return res;
}

void update(int x, ll d)
{
    while(x > 0)
    {
        tree[x] = (tree[x]*d) % mod;
        x -= lowbit(x);
    }
}

int main()
{
    scanf("%d", &n);
    for(int i = 1;i <= n;i++)
    {
        scanf("%lld%lld", &e[i].p, &e[i].d);
        d[i-1] = e[i].d;
    }
    ll inv = qpow(100, mod - 2);
    sort(d, d+n);
    int cnt = unique(d, d+n) - d;
    for(int i = 1;i <= n;i++)
    {
        tree[i] = 1;
        e[i].d = lower_bound(d, d+cnt, e[i].d) - d + 1;
    }
    ll ans = 0;
    for(int i = 1;i <= n;i++)
    {
        ans = (ans + query(e[i].d)*e[i].p%mod*inv%mod) % mod;
        update(e[i].d, 1LL*(100 - e[i].p)*inv%mod);
    }
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/83148547