牛客网暑期ACM多校训练营(第五场)F.take (逆元+树状数组)

题目链接

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

Kanade has n boxes , the i-th box has p[i] probability to have an diamond of d[i] size.

At the beginning , Kanade has a diamond of 0 size. She will open the boxes from 1-st to n-th. When she open a box,if there is a diamond in it and it's bigger than the diamond of her , she will replace it with her diamond.

Now you need to calculate the expect number of replacements.

You only need to output the answer module 998244353.

Notice: If x%998244353=y*d %998244353 ,then we denote that x/y%998244353 =d%998244353

输入描述:

The first line has one integer n.

Then there are n lines. each line has two integers p[i]*100 and d[i].

输出描述:

Output the answer module 998244353

示例1

输入

3
50 1
50 2
50 3

输出

499122178

备注:

1<= n <= 100000

1<=p[i]*100 <=100

1<=d[i]<=10^9

题意:有N个不同大小的宝箱,每个宝箱有2个属性,第一个是打开的概率,第二个是宝箱大小(这里保证每个宝箱都有同其大小一样的宝石),初始时你的手上只有大小为0的宝石,若是打开了一个宝箱且大小大于你手上的宝石就一定要交换,求从第一个到第N个宝箱都尝试过后交换的数学期望.

题解:这里我们先分析下概率的问题,因为对于直接求交换次数的数学期望不好求,需要转换一下:

        学过概率论应该知道,对于这里的每个宝箱都是相互独立的,那么我们对其中某个宝箱分析,打开成功且交换(宝箱大小大于手上宝石),那么我们必须交换,假设该情况的概率是P,那么其对立事件的概率就是1-P ,那么分析下该宝箱对整体交换次数的期望贡献值为: P*1+(1-P)*0=P.

        那么我们只需要求每个宝箱打开且交换的概率总和等同于求交换次数的数学期望了~

        对于求每个宝箱的打开且交换的概率,我们需要用到树状数组维护前缀积(这里的前缀积指的是1~i-1(当前宝箱前一个宝箱)中比当前宝箱大且都打不开的概率的乘积),那么我们只需要将query到的这个前追积乘上当前保险打开的概率即可~

        具体实现起来需要对宝箱预先编个号排序(按宝箱大小降序),处理树状数组时query(x)即可得到第1到i-1个宝箱的前缀积~

        剩下的就是简单的将代码写成逆元的形式即可(即每次的百分比的使用都*inv[100]);

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<fstream>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn = 1e5 + 10;
const ll mod = 998244353;
struct node {
	ll p, size;
	int x;
	bool operator < (const node &b)const{//按照size降序排序
		if (size == b.size) return x < b.x;
		return  size > b.size;
	}
}a[maxn];
//费马小定理求逆元(求a对于mod的逆元,要求mod为素数)
ll power_mod(ll a, ll b)//原理:求a^(mod-2)对模为mod的快速幂
{
	ll ans = 1;
	while (b)
	{
		if (b & 1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
int n;
ll inv;
ll tre[maxn];
void init() {
	for (int i = 0; i <= n; i++)
		tre[i] = 1;
}
int lowbit(int x) {               //获得x的二进制最后一个1的数值
	return x&(-x);
}
void update(int x, ll y) {        //更新
	for (int i = x; i <= n; i += lowbit(i))
		tre[i] = (tre[i] * y) % mod;
}
ll query(int x) {                 //1*(x的前缀积概率)=期望(贡献)
	ll res = 1;
	for (int i = x; i > 0; i -= lowbit(i))
		res = (res*tre[i]) % mod;
	return res;
}
int main() {
	inv = power_mod(100, mod - 2); 
	scanf("%d", &n);
	init();
	for (int i = 1; i <= n; i++)
		scanf("%lld%lld", &a[i].p, &a[i].size), a[i].x = i;
	sort(a + 1, a + n + 1);
	ll ans = 0;
	for (int i = 1; i <= n; i++) {
		ans = (ans + ((a[i].p*inv) % mod*query(a[i].x - 1)) % mod) % mod;
		update(a[i].x, ((100 - a[i].p)*inv) % mod);
	}
	printf("%lld\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/81430277