【组合数学】LGP3330(ZJOI2011)+UOJ#209
2017-12-12 16:09
344 查看
LGP3330 看电影(MOVIE)
原题地址【题目大意】
一共有n个人,k个格子(格子标号为1~k)
循环n轮,每轮随机一个k以内的整数(设为m),若m~k这些格子里有空的,就把一个人放到格子里,否则这个人将站着。
问全部人都坐着的概率是多少。
【题目分析】
一眼组合数学,实在不会可以考虑打表。
组合数学中经典的古典概型,概率等于合法的方案数除以总方案数。
【解题思路】
我们易得总方案数=KN。
合法方案数的计算:考虑在最后面加一个座位,然后所有的座位连成一个环。
这样我们保证不论怎么放,最后都会有一个空的位置,那么我们从这个空的位置断开(将它丢到),必然是一个合法的情况。
现在那么总方案数等于(K+1)N,每种环都算了(K+1)次,所以除以(K+1),最后抽调一个空白的座位来构造一个合法的方案,所以要乘(N−K+1)。
综上所述,答案就是:
(K+1)N−1×(N−K+1)KN
要用高精度 。【代码】
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=205; const int MAXL=505; int prinum,T,n,k,len; int a[MAXL],pri[MAXN],ans[MAXN],ans2[MAXN]; int p[MAXN][MAXN]; bool ispri[MAXN]; inline void get_prime() { memset(ispri,true,sizeof(ispri)); ispri[1]=false; for(int i=2;i<MAXN;++i) { if(ispri[i]) { ++prinum; pri[prinum]=i; } for(int j=1;j<=prinum && pri[j]*i<MAXN;++j) { ispri[pri[j]*i]=false; if(i%pri[j]==0) break; } } } inline void get_num() { int x; for(int i=1;i<MAXN;++i) { x=i; for(int j=1;j<=prinum;++j) while(x%pri[j]==0) { p[i][pri[j]]++; x/=pri[j]; } } } inline void _reset() { memset(ans,0,sizeof(ans)); memset(ans2,0,sizeof(ans2)); } inline void mul(int x) { if(len==0) { len=1;a[len]=x; for(len++;a[len];) { a[len+1]+=a[len]/10; a[len++]%=10; } --len; } else { for(int i=1;i<=len;++i) a[i]*=x; for(int i=1;i<=len;++i) { a[i+1]+=a[i]/10; a[i]%=10; } for(len++;a[len];) { a[len+1]+=a[len]/10; a[len++]%=10; } --len; } } inline void get_ans() { /* for(int i=1;i<10;++i) printf("%d ",ans[i]); printf("\n"); for(int i=1;i<10;++i) printf("%d ",ans2[i]); printf("\n");*/ len=0; memset(a,0,sizeof(a)); for(int i=1;i<MAXN;++i) for(int j=1;j<=ans[i];++j) mul(i); if(!len) printf("1"); for(int i=len;i;--i) printf("%d",a[i]); printf(" "); len=0; memset(a,0,sizeof(a)); for(int i=1;i<MAXN;++i) for(int j=1;j<=ans2[i];++j) mul(i); if(!len) printf("1"); for(int i=len;i;--i) printf("%d",a[i]); printf("\n"); } inline void solve() { scanf("%d%d",&n,&k); if(n>k) { printf("0 1\n"); return; } for(int i=1;i<=k+1;++i) ans[i]=p[k+1][i]*(n-1); if(k-n>0) for(int i=1;i<=k-n+1;++i) ans[i]+=p[k-n+1][i]; if(k>1) for(int i=1;i<=k;++i) { ans2[i]=p[k][i]*n-ans[i]; ans[i]-=p[k][i]*n; } get_ans(); } int main() { freopen("LGP3330.in","r",stdin); freopen("LGP3330.out","w",stdout); get_prime(); get_num(); scanf("%d",&T); while(T--) { _reset(); solve(); } return 0; }
UOJ#209. 【UER #6】票数统计
原题地址【题目大意】
一共有n个数字 ,每个数字要么是0,要么是1。
有m个限制(x,y),表示前x个数字里有y个1,或后y个数字里有x个1
求满足所有限制的序列个数。
n≤5000,m≤1000,答案对998244353取模,一个点至多5组数据
【题目分析】
都是骗人的!
【解题思路】
当x!=y的时候,我们已经确定了这个限制是对前缀还是后缀的。
当x=y的时候,我们只需要保留最大的那个x就行了。
然后,一波组合数学。
答案是x=y是前缀限制的个数+后缀限制的个数-前后缀都限制的个数。
那么,现在就是有一堆前后缀的限制,让你求方案数。
如果只有前缀或只有后缀就是一个划分成m个区间的组合数学。
如果两个都有就把前缀转成后缀或者把后缀转成前缀。
枚举1的总和就行了。
【代码】
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int MAXN=5010; const int mod=998244353; int n,m,ans,tot,lz,T; int C[MAXN][MAXN]; struct Tnode { int x,y,bz,ty; Tnode(){} Tnode(int xx,int yy,int bzz,int tyy) { x=xx;y=yy; bz=bzz;ty=tyy; } }; Tnode a[MAXN],p[MAXN]; inline void get_C() { C[0][0]=1; for(int i=1;i<MAXN-5;++i) { C[i][0]=1; for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } } inline bool cmp(Tnode A,Tnode B) { return A.x<B.x; } inline void init() { scanf("%d%d",&n,&m); tot=ans=lz=0; for(int i=1;i<=m;++i) { int x,y; scanf("%d%d",&x,&y); if(x>y) a[++tot]=(Tnode){x,y,0,0}; else if(x<y) a[++tot]=(Tnode){n-y,x,1,0}; else lz=max(lz,x); } a[++tot]=(Tnode){lz,lz,0,1}; a[++tot]=(Tnode){n-lz,lz,1,2}; sort(a+1,a+tot+1,cmp); // for(int i=1;i<=tot;++i) // printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].bz,a[i].ty); } inline void solve() { for(int typ=1;typ<=3;++typ) { for(int i=1;i<=n;++i) { int cnt=0;bool flag=true; for(int j=1;j<=tot;++j) { if(a[j].ty==typ) continue; p[++cnt].x=a[j].x; if(a[j].bz) p[cnt].y=i-a[j].y; else p[cnt].y=a[j].y; if(p[cnt].y<0 || p[cnt].y>i) { flag=false; break; } if(p[cnt].x<p[cnt].y) { flag=false; break; } if(p[cnt].x-p[cnt-1].x<p[cnt].y-p[cnt-1].y) { flag=false; break; } if(p[cnt].y-p[cnt-1].y<0) { flag=false; break; } } if(!flag) continue; int sum=1; for(int j=1;j<=cnt;++j) sum=1ll*sum*C[p[j].x-p[j-1].x][p[j].y-p[j-1].y]%mod; sum=1ll*sum*C[n-p[cnt].x][i-p[cnt].y]%mod; // printf("sum:%d\n",sum); if(typ<=2) ans=(ans+sum)%mod; else ans=(ans-sum+mod)%mod; } } printf("%d\n",ans); } int main() { // freopen("UOJ209.in","r",stdin); // freopen("UOJ209.out","w",stdout); get_C(); scanf("%d",&T); while(T--) { init(); solve(); } return 0; }
相关文章推荐
- [组合数学] BZOJ 2227 [Zjoi2011]看电影(movie)
- [BZOJ2227][Zjoi2011][找规律][排列组合][数学]看电影(movie)
- [BZOJ2227][Zjoi2011]看电影(movie)(组合数学+高精)
- [BZOJ2111][ZJOI2010]Perm 排列计数(组合数学+lucas定理)
- BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+Lucas定理
- 【BZOJ2227】【ZJOI2011】看电影 [组合数][质因数分解]
- [BZOJ 2339][HNOI 2011]卡农(组合数学)
- bzoj 2111: [ZJOI2010]Perm 排列计数 (组合数学+Lucas定理)
- 【noip2011】【数学】计算系数 逆元求组合数
- BZOJ 2339 HNOI2011 卡农 组合数学
- BZOJ2302: [HAOI2011]Problem c|动态规划|组合数学
- BZOJ 2111: [ZJOI2010]Perm 排列计数(简单组合数学)
- BZOJ 2111: [ZJOI2010]Perm 排列计数|组合数学|Lucas定理|DP
- [BZOJ2111][ZJOI2010]Perm排列计数(组合数学)
- [bzoj2111][ZJOI2010]Perm 排列计数(组合数学)
- bzoj 2339: [HNOI2011]卡农 组合数学+递推
- BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+Lucas定理
- 【bzoj2339】【HNOI2011】【卡农】【组合数学+dp】
- BZOJ 2302([HAOI2011]Problem c-组合数学)
- Luogu 1313(组合数学)(NOIP2011)