来自星星的祝福(容斥+排列组合)

来自星星的祝福

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 102 Accepted Submission(s): 9

Problem Description

在一个遥远的山区中,流传着一个传说,一个真正的好男人,需要受到来自M个不同星座的女孩子的赞美--“你是个好人”,才会找到真爱。
毫无疑问,yyf就是这样的好男人。他费尽千辛万苦,找到了瞎子算命师ZJiaQ,ZJiaQ告诉他:你这辈会受到n个女孩子的赞美。
那么,请问yyf找到真爱的概率有多少?
假设每个女孩子是M个星座中任一个的概率相等。

Input

第一行一个数T(T<=10),表示数据组数。(T可输入多次)
接下来每组数据中,输入n与m(n<=1000000000000000000(18个零),m<=100)。

Output

输出概率的百分比,并且不需要输出小数点后的数字。

Sample Input

2
1 1
2 2

Sample Output

%100
%50

分析

有两种思路
1.套用容斥定理,我们可以得到如下公式

\[1+\sum_{i=m-1}^{1}(-1)^{m-i}(\frac{i}{m})^nC_m^i \]

带入计算即可
2.令f[i]表示n个人恰好有i个星座的概率,那么f[i]=(m个星座选指定的i个的概率)^n-(i个人选了j个星座的概率)*(j个星座的排列组合
代入计算即可

trick

1.可能出现ans小于0,此时ans变成0

代码

//思路1
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))
#pragma comment(linker, "/STACK:102400000,102400000")
inline void read(int &x){x=0; char ch=getchar();while(ch<'0') ch=getchar();while(ch>='0'){x=x*10+ch-48; ch=getchar();}}

double f[101];
int t;
ll n,m;
double work(double x,ll n,ll z)
{
    double ret=f[m]/f[z]/f[m-z];
    for(;n;n>>=1,x*=x) if(n&1) ret*=x;
        return ret;
}
int main()
{
    f[0]=1;
    for(ll i=1;i<=100;++i) f[i]=f[i-1]*i; 
    while(~scanf("%d",&t))
    {
        while(t--)
        {
            scanf("%lld %lld",&n,&m);
            if(n<m) {printf("%%0\n");continue;}
            double ans=1;
            for(int i=m-1;i;--i)
            {
                double ret=work(1.0*i/m,n,i);
                ans+=(((m-i)&1)?-1:1)*ret;
            }
            if(ans<0) ans=0;
            putchar('%');
            printf("%.f\n",ans*100);
        }
    }
    return 0;
}
//思路2
#include<bits/stdc++.h>
using namespace std;
typedef __int64 ll;

long double c[105][105];
ll n,m;
long double f[105];
long double kp(long double x,ll n)
{
    long double ret=1;
    while(n)
    {
        if (n%2==1) ret*=x;
        n/=2;
        x*=x;
    }
    return ret;
}
int main()
{
    c[0][0]=1;
    for (int i=1;i<=10;i++)
    {
        c[i][0]=1;
        for (int j=1;j<=i;j++)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
    int t;

    while(~scanf("%d",&t))
    {

        while(t--)
        {
            scanf("%I64d%I64d",&n,&m);
            if (n<m) {printf("%%0\n"); continue;}
            for (int i=1;i<=m;i++)
            {
                f[i]=kp(1.0*i/m,n);
                for (int j=1;j<i;j++) f[i]-=f[j]*c[i][j];
            }
            if (f[m]<0) f[m]=0;
            printf("%%%.0f\n",(double)f[m]*100);
        }
    }
}
posted @ 2017-07-02 23:21  遗风忘语  阅读(183)  评论(0编辑  收藏  举报