FZU-2103(枚举+组合数学)

题意:

大致意思为,给定n个编号的出现概率。在这n个编号中有放回的取k次,现已知编号按大到小排后前r次的编号,求出现该种情况的概率。

题解:

做这道题我们需要知道一个求这种有放回,且存在相同物品的概率公式即 ( k ! ) / ( n u m 1 ! ∗ n u m 2 ! ∗ n u m 3 ! . . . . . . ) (k!)/(num1!*num2!*num3!......) (k!)/(num1!num2!num3!......)其中num1-n表示k次取种这n种物品出现的次数。

在这个知识前提下观察题目,发现已知序列最大的r次选取,那么剩下的编号一定小于等于r次种出现的最小值(此处记为minn)。继而我们又可以发现只有当之后的编号是minn上述公式种的num_minn是从r次中的个数加上剩余部分中minn的个数。 而编号比minn小的部分在公式中都是从1开始的,与前置已知条件无关,于是我们就可以想到将编号小于minn看作一个整体 其整体的概率就是他们概率之和,个数就是剩余部分-minn个数。这样我们只要枚举剩余部分minn出现的次数,通过第一段的公式求出概率值再求一次和便是答案

代码:

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <ctime>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define PB push_back
#define MP make_pair
#define INF 1073741824
#define inf 1152921504606846976
#define pi 3.14159265358979323846
//#pragma comment(linker,"/STACK:10240000,10240000")
const int N=3e5+7,M=2e6;
const long long mod=1e9+7;
inline int read(){int ret=0;char ch=getchar();bool f=1;for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-48;return f?ret:-ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll ksm(ll a,ll b,ll mod){int ans=1;while(b){if(b&1) ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;}return ans;}
ll inv2(ll a,ll mod){return ksm(a,mod-2,mod);}//逆元
//int head[N],NEXT[N],ver[N],tot;void link(int u,int v){ver[++tot]=v;NEXT[tot]=head[u];head[u]=tot;}

db a[N];
ll num[30];
int main(){
    //freopen("1.txt","r",stdin);
    int t;
    int n,k,r;
    int inp;
    scanf("%d",&t);
    while(t--){
        db res=1.0;
        memset(num,0,sizeof(num));
        scanf("%d%d%d",&n,&k,&r);
        for(int i=1;i<=n;i++){
            scanf("%lf",&a[i]);
        }
        int minn=INF;
        for(ll i=1;i<=r;i++){
            scanf("%d",&inp);
            res*=a[inp];
            minn=min(inp,minn);
            num[inp]++;
            res*=(db)i;
            res/=(db)num[inp];
        }
        db tmp;
        db tt=0.0;
        db ans=0.0;
        for(int i=1;i<minn;i++){
            tt+=a[i];
        }
        for(int i=0;i<=k-r;i++){
            tmp=res;
            for(int j=1;j<=i;j++){
                tmp*=(db)(j+r);
                tmp*=a[minn];
                tmp/=(db)(num[minn]+j);
            }
            for(int p=i+1;p<=k-r;p++){
                tmp*=(db)(p+r);
                tmp*=tt;
                tmp/=(db)(p-i);
            }
            ans+=tmp;
        }
        printf("%.6f\n",ans);

    }

    //cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值