• [51nod 1850] 抽卡大赛


    问题描述

    51Nod为了活跃比赛前的气氛,组织了场抽卡比赛。这场比赛共 n 个人参加,主办方根据非欧血统鉴定器,得到了一些数据。每个人抽卡有 Mi 种可能,得到的卡能力值为 Aij 代价为 Gij 的可能性为 Pij ,所谓代价指的是玩家需要将一轮比赛后所得的点头盾的 Gij% 交给主办方。每轮比赛每个人都随机抽取卡片,待全部人抽取完毕后进行排名(按照A从大到小排),排在第 i 位的人有 Vi 的点头盾收入。现在主办方想知道一轮比赛后每个人的期望收入。

    输入格式

    第一行一个正整数 n
    接下来 n 个部分
    每个部分第一行为正整数 Mi,接下来 Mi 行有三个整数 Aij Gij Pij
    接下来一行 n 个整数,分别为 Vi
    设 ∑Pij=Qi,则第 i 个人抽到第 j 张卡的概率为 Pij/Qi
    1<=n,Mi<=200,1<=Aij<=1000000000,保证 Aij 互不相同,0<=Gij<=100,1<=Pij<=1000,1<=Vi<=1000

    输出格式

    输出 n 行,每行一个数表示每个人的期望收入
    为了防止精度误差,输出答案在模 1e9+7 意义下的数值

    样例输入

    2
    2
    3 50 5
    4 50 5
    2
    5 50 5
    6 50 5
    2 2

    样例输出

    1
    1

    解析

    O(n^4)的做法比较好想。回想这个做法,可以发现最麻烦的一点是求每个人排名多少的概率。考虑优化这一过程。

    假设当前考虑第(i)个人。设(p_j)表示第(j)个人排名比(i)靠前的概率,(f[j])表示第(i)个人排名为j的概率。结合生成函数的知识,(f)数列的生成函数即为

    [prod_{j!=i}p_j imes x+(1-p_j) ]

    (f[i])即对应上面多项式展开后的i-1次项。所以,我们将所有卡牌按照a从大到小排序,如果当前卡牌对应的人的排名要往后,就必须要在这张牌之前的牌中选一张。我们需要维护上面的多项式。具体的,设当前人为i,那么(p[j])就是j前面出现的所有卡牌的概率之和。由于多项式满足(i!=j),我们要将当前的多项式除以(p_i imes x+1-p_i),然后乘上(p_j imes x+1-p_j)。手动模拟一下两个多项式相乘的过程就可以解决这个问题了。

    当然也可以NTT

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define int long long
    #define N 202
    using namespace std;
    const int mod=1000000007;
    struct card{
    	int a,g,p,id;
    }a[N*N];
    int n,m[N],v[N],cnt,i,j,f[N],ans[N],p[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    int poww(int a,int b)
    {
    	int ans=1,base=a;
    	while(b){
    		if(b&1) ans=ans*base%mod;
    		base=base*base%mod;
    		b>>=1;
    	}
    	return ans;
    }
    int my_comp(const card &x,const card &y)
    {
    	return x.a>y.a;
    }
    signed main()
    {
    	n=read();
    	for(i=1;i<=n;i++){
    		m[i]=read();
    		int inv=0;
    		for(j=1;j<=m[i];j++){
    			cnt++;
    			a[cnt].a=read(),a[cnt].g=read(),a[cnt].p=read();
    			a[cnt].id=i;
    			a[cnt].g=(100-a[cnt].g)*poww(100,mod-2)%mod;
    			inv=(inv+a[cnt].p)%mod;
    		}
    		inv=poww(inv,mod-2);
    		for(j=1;j<=m[i];j++) a[cnt-j+1].p=a[cnt-j+1].p*inv%mod;
    	}
    	for(i=1;i<=n;i++) v[i]=read();
    	sort(a+1,a+cnt+1,my_comp);
    	f[1]=1;
    	for(i=1;i<=cnt;i++){
    		if(a[i-1].id!=a[i].id){
    			int inv=poww(1-p[a[i].id]+mod,mod-2);
    			f[1]=f[1]*inv%mod;
    			for(j=2;j<=n;j++) f[j]=(f[j]-f[j-1]*p[a[i].id]%mod+mod)%mod*inv%mod;
    			for(j=n;j>=1;j--) f[j]=(f[j]*(1-p[a[i-1].id]+mod)%mod+f[j-1]*p[a[i-1].id]%mod)%mod;
    		}
    		for(j=1;j<=n;j++) ans[a[i].id]=(ans[a[i].id]+f[j]*v[j]%mod*a[i].p%mod*a[i].g%mod)%mod;
    		p[a[i].id]=(p[a[i].id]+a[i].p)%mod;
    	}
    	for(i=1;i<=n;i++) printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    java基础_面试题笔记
    ACM-ICPC 2018 Xuzhou Online Contest题解
    覆盖点问题总结
    2018icpc沈阳网络赛题解(转发)
    树链剖分
    树状数组
    线段树板子
    sdoi2016生成魔咒
    洛谷3804
    大佬博文收集
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12616610.html
Copyright © 2020-2023  润新知