【BZOJ2227】【ZJOI2011】看电影 [组合数][质因数分解]
看电影
Time Limit: 10 Sec Memory Limit: 259 MB[Submit][Status][Discuss]
Description
到了难得的假期,小白班上组织大家去看电影。但由于假期里看电影的人太多,很难做到让全班看上同一场电影,最后大家在一个偏僻的小胡同里找到了一家电影院。但这家电影院分配座位的方式很特殊,具体方式如下: 1. 电影院的座位共有K个,并被标号为1…K,每个人买完票后会被随机指定一个座位,具体来说是从1…K中等可能的随机选取一个正整数,设其为L。 2. 如果编号L的座位是空位,则这个座位就分配给此人,否则将L加一,继续前面的步骤。 3. 如果在第二步中不存在编号L的座位,则该人只能站着看电影,即所谓的站票。小白班上共有N人(包括小白自己),作为数学爱好者,小白想知道全班都能够有座位的概率是多少。
Input
输入文件第一行有且只有一个正整数T,表示测试数据的组数。 第2~T+1行,每行两个正整数N,K,用单个空格隔开,其含义同题目描述。
Output
输出文件共包含T行。第i行应包含两个用空格隔开的整数A,B,表示输入文件中的第i组数据的答案为A/B。(注意,这里要求将答案化为既约分数)
Sample Input
31 1
2 1
2 2
Sample Output
1 10 1
3 4
HINT
对于100%的数据 T<=50,N,K<=200Main idea
有n个人,k个位置,询问按照以下坐法使得所有人都有位置坐的概率是多少。(坐法:每个人随机一个位置,如果这个位置有人那一直就往后坐,如果后面都有人了则不可行)
Source
运用组合数学,首先我们知道n个人k个位置的总方案数是k^n,然后我们考虑一下怎么求出可行的方案,发现直接做的话无解与有解的两个情况不好考虑,怎么办呢?
我们发现可以考虑一下多加一个空位置使其构成一个环,那么这时候每个位置都必定是有解的,方案数就是(k+1)^n。再考虑如何删掉重复的情况,由于我们加入了一个位置,那么除去经过这个位置的情况显然是方案数/(k+1),那么现在方案数就是(k+1)^n/(k+1),然后乘上有几个空位置即可。最后答案就是:( (k+1)^n/(k+1)*(k+1-n) ) / (k^n)。
我们发现这个现在求出来的方案数比较大,但是又看见了n,k<=200,想到了数字n的质因数和n^k的质因数是一样的(所以这时候质因数肯定都是200以内的质数),所以我们乘的时候直接质因数分解,然后两个数字的质因数去重,最后用一个高精度乘起来输出即可。
Code
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> using namespace std; const int ONE=201; int T; int n,k; int prime[ONE],cnt; int p[ONE][3]; int record,x; struct power { int num[10001],len; void print() { for(int i=len;i>=1;i--) printf("%d",num[i]); } friend power operator *(power a,power b) { power c; c.len=a.len+b.len; for(int i=1;i<=c.len;i++) c.num[i]=0; for(int i=1;i<=a.len;i++) { x=0; for(int j=1;j<=b.len;j++) { c.num[i+j-1]=c.num[i+j-1] + x + a.num[i]*b.num[j]; x=c.num[i+j-1]/10; c.num[i+j-1]%=10; } c.num[i+b.len]=x; } while(c.len>1 && !c.num[c.len]) c.len--; return c; } }kd[3],pass; void dealwith(int x) { for(int i=1;i<=pass.len;i++) pass.num[i]=0; pass.len=0; while(x) { pass.num[++pass.len]=x%10; x/=10; } } int get() { int res=1,Q=1;char c; while( (c=getchar())<48 || c>57 ) if(c=='-')Q=-1; res=c-48; while( (c=getchar())>=48 && c<=57 ) res=res*10+c-48; return res*Q; } int PD(int x) { for(int i=2;i<x;i++) if(x%i==0) return 0; return 1; } int Chai(int x,int PD) { if(x==1) return x; for(int i=1;i<=cnt;i++) { if(!(x%prime[i])) { p[prime[i]][PD]++; x=Chai(x/prime[i],PD); break; } } return x; } int Deal(int x,int m,int PD) { record=1; for(int i=1;i<=m;i++) { record*=x; record=Chai(record,PD); } if(PD==1) { record*=(x-m-1); record=Chai(record,PD); } p[record][PD]++; } int main() { T=get(); for(int i=2;i<=200;i++) if(PD(i)) prime[++cnt]=i; while(T--) { n=get(); k=get(); if(n>k) { printf("0 1\n"); continue; } memset(p,0,sizeof(p)); Deal(k+1,n-1,1); Deal(k,n,2); for(int i=1;i<=200;i++) { int x=min(p[i][1],p[i][2]); p[i][1]-=x; p[i][2]-=x; } kd[1].len=kd[2].len=1; kd[1].num[1]=kd[2].num[1]=1; for(int i=1;i<=cnt;i++) { dealwith(prime[i]); for(int t=1;t<=2;t++) for(int j=1;j<=p[prime[i]][t];j++) kd[t]=kd[t]*pass; } kd[1].print(); printf(" "); kd[2].print(); printf("\n"); } }View Code
- [组合数学] BZOJ 2227 [Zjoi2011]看电影(movie)
- [BZOJ2227][Zjoi2011]看电影(movie)(组合数学+高精)
- [BZOJ2227][Zjoi2011][找规律][排列组合][数学]看电影(movie)
- BZOJ2227 [Zjoi2011]看电影(movie)
- ZJOI2011 Day1 ( bzoj2227~2229 ) 题解
- 2227: [Zjoi2011]看电影(movie)
- 2227: [Zjoi2011]看电影(movie)
- 【bzoj2229】 Zjoi2011—最小割
- bzoj2325 [ZJOI2011]道馆之战
- BZOJ 2229 ZJOI2011 最小割 最小割+分治 400AC达成&&2000Submission达成
- bzoj 2229 [Zjoi2011]最小割(分治+最小割)
- [BZOJ2324][ZJOI2011]营救皮卡丘
- BZOJ 2229 [Zjoi2011]最小割
- BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+Lucas定理
- 【bzoj2324】[ZJOI2011]营救皮卡丘 最短路-Floyd+有上下界费用流
- 【组合计数】【bzoj 3294】: [Cqoi2011]放棋子
- bzoj 2323: [ZJOI2011]细胞
- [BZOJ2229][Zjoi2011]最小割(最小割+分治)
- BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+Lucas定理
- bzoj 2324 [ZJOI2011]营救皮卡丘(floyd,费用流)