牛客网暑期ACM多校训练营(第五场)--F take

题意

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

思路

结果要求的是交换次数的期望,我们可以将每个箱子交换的期望求出来相加即可。那么显而易见的是,当前箱子交换的概率即为该箱子之前的所有d[i]大于当前箱子的箱子内的钻石都不出现的概率乘以当前箱子出现钻石的概率。

思路一: 

我们可以首先将d从大到小排序,用树状数组维护1 - pi的前缀积。每次插入前查询在它前面比他大的概率的乘积(因为从大到小插入了,所以树里面的插入的都是d比他大的)由于维护的是乘积,所以初值要初始化成1。

思路二:

我们还可以将d离散化,然后用一个大数去减离散化后的d,(原来维护比它大的值就变成了维护比它小的值),这样就不需要按照d的大小排序了,直接按顺序处理,查询d-1的前缀和。

代码一:

#include 
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int mod=998244353;
#define lowbit(a) ((a)&(-(a)))
ll tree[200005];
ll n;
struct node
{
  ll b,id,p;
  bool friend operator <(node x,node y)
  {
      return x.b>y.b;
  }
}b[100005];
ll s[100005];
void add(int x,ll c){
    while(x<=n){
        tree[x]=(tree[x]*c)%mod;
        x += x&(-x);
    }
}
ll sum(int x){
    ll res = 1;
    while(x){
        res = (tree[x]*res)%mod;
        x -=x&(-x);
    }
    return res;
}
ll poww(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
	scanf("%lld",&n);
	ll inv=poww(100,mod-2);//100的逆元
	for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&b[i].p,&b[i].b);
        b[i].p=(b[i].p*inv)%mod;
        b[i].id=i;tree[i]=1;
    }
    sort(b+1,b+n+1);
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=(ans+sum(b[i].id)*b[i].p)%mod;
        add(b[i].id,(1-b[i].p+mod)%mod);
    }
    printf("%lld\n",ans);
	return 0;
}

代码二:

#include 
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int mod=998244353;
#define lowbit(a) ((a)&(-(a)))
ll tree[200005];
ll n;
ll p[100005],b[100005],bb[100005];
ll s[100005];
void add(int x,ll c){
    while(x<=n){
        tree[x]=(tree[x]*c)%mod;
        x += x&(-x);
    }
}
ll sum(int x){
    ll res = 1;
    while(x){
        res = (tree[x]*res)%mod;
        x -=x&(-x);
    }
    return res;
}
ll poww(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
    scanf("%lld",&n);
    ll inv=poww(100,mod-2);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&p[i],&b[i]);
        p[i]=(p[i]*inv)%mod;
        bb[i]=b[i];
    }
    for(int i=0;i<200005;i++)
        tree[i]=1;
    sort(bb+1,bb+n+1);
    int e=unique(bb+1,bb+1+n)-(bb+1);
    for(int i=1;i<=n;i++)
    {
        b[i]=100000-(lower_bound(bb+1,bb+1+n,b[i])-bb)+1;
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=(ans+sum(b[i]-1)*p[i])%mod;
        add(b[i],(1-p[i]+mod)%mod);
    }
    printf("%lld\n",ans);
    return 0;
}

 

你可能感兴趣的:(树状数组)