http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3957
题意:m个位置,每个位置填1~n的数,求至少有L个位置的数一样的概率(1<=n,m,l<=100)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct inum { static const int N=205, MOD=10000; int a[N], len; inum(int x=0) { len=!x; memset(a, 0, sizeof a); while(x) a[++len]=x%MOD, x/=MOD; } void fix() { while(len>1 && !a[len]) --len; } void cln() { memset(a, 0, sizeof(int)*(len+1)); len=1; } const bool operator<= (const inum &x) { if(len<x.len) return 1; if(len>x.len) return 0; for(int i=len; i; --i) if(a[i]<x.a[i]) return 1; else if(a[i]>x.a[i]) return 0; return 1; } inum operator+ (const inum &x) { static inum ret; ret.cln(); ret.len=max(len, x.len); for(int i=1; i<=ret.len; ++i) { ret.a[i]+=a[i]+x.a[i]; if(ret.a[i]>=MOD) ret.a[i+1]+=ret.a[i]/MOD, ret.a[i]%=MOD; } ret.len++; ret.fix(); return ret; } inum operator- (const inum &x) { static inum ret; ret.cln(); for(int i=1; i<=len; ++i) { ret.a[i]+=a[i]-x.a[i]; if(ret.a[i]<0) --ret.a[i+1], ret.a[i]+=MOD; } ret.len=len; ret.fix(); return ret; } inum operator* (const inum &x) { static inum ret; ret.cln(); for(int i=1; i<=len; ++i) for(int j=1; j<=x.len; ++j) { ret.a[i+j-1]+=a[i]*x.a[j]; if(ret.a[i+j-1]>=MOD) ret.a[i+j]+=ret.a[i+j-1]/MOD, ret.a[i+j-1]%=MOD; } ret.len=len+x.len; for(int i=1; i<=ret.len; ++i) if(ret.a[i]>=MOD) ret.a[i+1]+=ret.a[i]/MOD, ret.a[i]%=MOD; ret.fix(); return ret; } inum div2() { static inum ret; ret.cln(); for(int i=len, t=0; i; --i) { t+=a[i]; ret.a[i]=t>>1; t=(t&1)*MOD; } ret.len=len; ret.fix(); return ret; } inum operator/ (const inum &x) { static inum l, r, mid, ONE(1); l=0; r=*this; while(l<=r) { mid=(l+r).div2(); if(mid*x<=*this) l=mid+ONE; else r=mid-ONE; } return l-ONE; } inum operator% (const inum &x) { return *this-(*this/x)*x; } static inum gcd(inum a, inum b) { return (b.len==1&&!b.a[1])?a:gcd(b, a%b); } static inum pow(inum a, int n) { inum x=1; while(n) { if(n&1) x=x*a; a=a*a; n>>=1; } return x; } void P() { printf("%d", a[len]); for(int i=len-1; i; --i) printf("%.04d", a[i]); } }; const int N=105; inum C[N][N], d[N][N]; int n, m, l; int main() { C[0][0]=1; for(int i=1; i<=100; ++i) { C[i][0]=1; for(int j=1; j<=i; ++j) C[i][j]=C[i-1][j-1]+C[i-1][j]; } while(~scanf("%d%d%d", &m, &n, &l)) { if(l>m) { puts("mukyu~"); continue; } d[0][0]=1; --l; for(int i=1; i<=n; ++i) for(int j=1; j<=m; ++j) { d[i][j]=0; int t=min(j, l); for(int k=0; k<=t; ++k) d[i][j]=d[i][j]+d[i-1][j-k]*C[m-(j-k)][k]; } inum up=0, down=0, gcd; for(int i=1; i<=n; ++i) up=up+d[i][m]; down=inum::pow(n, m); up=down-up; gcd=inum::gcd(up, down); down=down/gcd; up=up/gcd; up.P(); putchar('/'); down.P(); puts(""); } return 0; }
(麻麻我又码出了一个高精度模板QAQ感觉这一次封包得很好QAQ
(反正超慢QAQ差点就tle了....6000+ms....
首先本题超神!我忘记了补集转化....
求至少L个位置的数一样的方案=总方案数-少于L个位置的数一样的方案数
而后者非常好求...
设$d[i][j]$表示前$i$种数在$m$个位置上填了$j$个的方案数
$$d[i][j]=\sum_{k=0}^{min(j, L-1)} d[i-1][j-k]C[m-(j-k)][k]$$
那么$ans=\frac{n^m-\sum_{i=1}^{n} d[i][m]}{n^m}$
由于输出分数形式...于是上高精度......