take(概率 树状数组)

原题:牛客网暑期ACM多校训练营

题意:

好像没报名不能进,这边直接拉吧
这里写图片描述
就是给n个盒子,每个盒子有p[i]/100的概率开出siz[i]大小的钻石,开始的时候你手里的大小为0,如果开出比手里钻石大的话就交换,求的是交换次数的期望

解析:

对于i,对于答案的贡献的情况是:前面没拿比这个钻石大(或者相同)的钻石的概率*拿这个钻石的概况

这个想出来的话就异常简单了,用树状数组维护一下就行了

代码:


typedef long long D;
typedef double F;
const D mod=998244353;
const int N=100000;
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}


D swift(D a,D b){
    D ans=1ll;
    while(b){
        if(b%2)ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }return ans;
}
D inv(D a){return swift(a,mod-2);}
D a[100009];
const D inv100=inv(100);

struct node{
    D p,s,id;
    bool operator<(const node &a)const{
        return s>a.s||s==a.s&&id<a.id;
    }
}e[100009];

void add(D p,D v,D n){
    while(p<=n){
        a[p]=a[p]*v%mod;p+=p&(-p);
    }
}

D q(D p){
    D re=1;
    while(p>0){
        re=re*a[p]%mod,p-=p&(-p);
    }return re;
}

int main(){
    D n=read();
    for(int i=0;i<=n;i++)a[i]=1;
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&e[i].p,&e[i].s);e[i].id=i;
    }
    D sum=0;
    sort(e+1,e+1+n);
    for(int i=1;i<=n;i++){
        D tmp=q(e[i].id-1)*(e[i].p*inv100%mod)%mod;
        sum=(sum+tmp)%mod;
        add(e[i].id,(100-e[i].p)*inv100%mod,n);
    }
    printf("%lld\n",sum);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/81409077