链接:https://ac.nowcoder.com/acm/contest/549/C
来源:牛客网
题目描述
小A最近开始沉迷买彩票,并且希望能够通过买彩票发家致富。已知购买一张彩票需要3元,而彩票中奖的金额分别为1,2,3,4元,并且比较独特的是这个彩票中奖的各种金额都是等可能的。现在小A连续购买了n张彩票,他希望你能够告诉他至少能够不亏本的概率是多少。
输入描述:
一行一个整数N,为小A购买的彩票数量
输出描述:
输出一个最简分数a/b,表示小A不亏本的概率。若概率为1,则输出1/1,概率为0,则输出0/1。
输入
2
输出
3/8
备注:
0≤n≤30
题解
牛客网给出的题解:
考虑买n张彩票的总的方案数是4^n,然后统计不亏本的方案数,记录f[i][j]是买到第i张彩票总获利为j的总方案数。f[i][j]=∑f[i−1][j−k],k=1,2,3,4最后统计一下不亏本的方案数即可。由于数据规模很小,考虑分别组合枚举有多少个1,2,3,4也可以通过。
复杂度:O(n^2)
我当时没想到怎么dp,我组合枚举1,2,3,4的个数做的,复杂度O(n^3). 设1,2,3,4的个数分别为a,b,c,d,那么不亏的情况下应该满足 2a+b<d并且a+b+c+d==0, 满足上述条件下不亏的方案数应该是 a个1,b个2,c个3,d个4的全排列的个数。全排列的个数应该等于 n!/(a!*b!*c!*d!). 枚举a,b,c就可以算出所有不亏的方案数。 总的方案数应该等于4^n,等于2^(2n),直接通过位运算就可以算出来 long long sum=1LL<<2*n.然后算一下gcd约分就可以了。注意预处理阶乘的时候30的阶乘爆long long,那么可以用
long double 存储阶乘。显然不亏的情况数小于总方案数sum,所以最后再把long double 转成long long 计算gcd。
或者用剔除质因子的方法算,把n!/(a!*b!*c!*d!) 分解质因子。这样就不会爆long long。每个大于2的正整数都可以化成质数相乘的式子。
下边给出枚举1,2,3,4个数的代码和枚举1,2,3,4个数剔除质因子和dp代码:
枚举1,2,3,4个数,long double存储的代码:
#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
typedef long long ll;
int n;
long double ans;
long double f[maxn]; //阶乘
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main(){
cin>>n;
f[0]=1;
for(int i=1;i<=n;i++) f[i]=i*f[i-1];
for(int a=0;a<=n;a++){
for(int b=0;b<=n;b++){
for(int c=0;c<=n;c++){
int k=n-a-b-c;
if(k<0) continue;
if(2*a+b<=k){
ans+=f[n]/f[a]/f[b]/f[c]/f[k];
}
}
}
}
ll ans1=(long long)(ans);
ll y=1LL<<2*n;
ll x=gcd(ans1,y);
printf("%lld/%lld\n",ans1/x,y/x);
return 0;
}
枚举1,2,3,4个数,剔除质因子:
#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
typedef long long ll;
int n;
ll ans;
int p[30];
int prime[10]={2,3,5,7,11,13,17,19,23,29};
int cnt=10; //质数个数
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
//算x的阶乘有多少因子y
int get(int x,int y){
int s=0;
while(x){
s+=x/y;
x/=y;
}
return s;
}
ll ksm(ll x,ll y){
ll ans=1;
while(y){
if(y&1) ans*=x;
y>>=1;
x*=x;
}
return ans;
}
int main(){
cin>>n;
for(int a=0;a<=n;a++){
for(int b=0;b<=n;b++){
for(int c=0;c<=n;c++){
int k=n-a-b-c;
if(k<0) continue;
if(2*a+b<=k){
for(int i=0;i<cnt;i++) p[prime[i]]=0;
for(int i=0;i<cnt;i++){
p[prime[i]]=get(n,prime[i])-get(a,prime[i])-get(b,prime[i])-get(c,prime[i])-get(k,prime[i]);
}
ll x=1;
for(int i=0;i<cnt;i++){
if(p[prime[i]]){
x*=ksm(prime[i],p[prime[i]]);
}
}
ans+=x;
}
}
}
}
ll y=1LL<<2*n;
ll z=gcd(ans,y);
printf("%lld/%lld\n",ans/z,y/z);
return 0;
}
dp代码:
#include<algorithm>
#include <iostream>
#include<cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=5e6+5;
ll dp[35][125];
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main(){
int n;
cin>>n;
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=i;j<=4*i;j++){
for(int k=1;k<=4;k++){
if(j-k>=0) dp[i][j]+=dp[i-1][j-k];
}
}
}
ll x=0;
for(int i=3*n;i<=4*n;i++){
x+=dp[n][i];
}
ll y=1LL<<2*n;
ll z=gcd(x,y);
printf("%lld/%lld\n",x/z,y/z);
return 0;
}