HDU--5160(组合数)
2015-01-18 14:13:58
思路:由于期末考,这道题拖了好久才补掉...
BC题解说的很好了,参考下。
数据规模比较大,直接暴力行不通。可以对不同的数字进行独立的处理。
先对数据进行排序,然后把每一种数字有多少个统计一下。
因为对于比当前小的数字是不影响当前数字是否被统计的。所以可以只考虑比当前数字大的,记当前要统计的是第i种数字,数字为x,有y个,比当前数字大的有tot个,总共有n个数字。可以先从n个位置中选出y+tot个位置给当前数字和比当前数字大的数。剩下的位置给前面i-1种数字放,前面i-1种数字的排列数可以通过多重集排列轻松计算出来。Dp[i]代表前i种数字的排列数。Dpr[i]代表后i种数字的排列数。接下来就是计算当前数字有几个个被统计进去了。
可以枚举当前数字被统计进去的数目那么数字x被统计进去的总和是
C(n,y+tot)*Dp[i-1]*x*(y+(y-1)*c(tot,1)+(y-2)*c(tot+1,2)+…+1*c(tot+y-2,y-1))*dpr[i+1]
C(i,j)为组合数从i个中选j个的方法数。
最后总的复杂度是O(nlogn)
需要补充的是:(1)多重集排列的计算方法是:A(n) / (A(num[1] * A(num[2]) * A(num[3]...),就是总数的全排列除掉部分全排列。num表示每个数的个数。
(2)关于组合数的计算,因为n的范围比较大,不能直接二维数组暴力存储,要用到C(n,m) = n!/((n-m)!*m!),因为涉及到取模,所以要求分母的逆元,这里用到费马小定理:gcd(a,p) = 1 --> a^(p-1) ≡ 1(mod p),那么a 与 a^(p-2) 互为逆元,所以要模快速幂求出分母的(p-2)次方得到它的逆元,题目里p为1e9+7。
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <iostream> 11 #include <string> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define MP(a,b) make_pair(a,b) 21 22 typedef long long ll; 23 typedef pair<int,int> pii; 24 const int INF = (1 << 30) - 1; 25 const ll mod = 1e9 + 7; 26 const int maxn = 1e5 + 10; 27 28 int T,n,A[maxn],sumnum,num[maxn],cur; 29 ll fac[maxn],afac[maxn],cnt[maxn],rcnt[maxn]; 30 31 ll Q_pow(ll x,ll y){ 32 ll res = 1; 33 while(y){ 34 if(y & 1) res = (res * x) % mod; 35 x = (x * x) % mod; 36 y >>= 1; 37 } 38 return res; 39 } 40 41 void Pre(){ 42 fac[0] = afac[0] = 1; 43 REP(i,100000){ 44 fac[i] = (fac[i - 1] * (ll)i) % mod; 45 afac[i] = Q_pow(fac[i],mod - 2); 46 } 47 } 48 49 void Read(){ 50 scanf("%d",&n); 51 REP(i,n) scanf("%d",A + i); 52 sort(A + 1,A + n + 1); 53 cur = 0; 54 MEM(num,0); 55 sumnum = 0; 56 REP(i,n){ 57 if(A[i] != A[i - 1]){ 58 A[++cur] = A[i]; 59 num[cur] = 1; 60 } 61 else num[cur]++; 62 } 63 REP(i,cur) sumnum += num[i]; 64 int sum = 0; 65 ll bot = 1; 66 REP(i,cur){ 67 sum += num[i]; 68 bot = (bot * afac[num[i]]) % mod; 69 cnt[i] = (fac[sum] * bot) % mod; 70 } 71 sum = 0; 72 bot = 1; 73 REV(i,cur){ 74 sum += num[i]; 75 bot = (bot * afac[num[i]]) % mod; 76 rcnt[i] = (fac[sum] * bot) % mod; 77 } 78 cnt[0] = 1; 79 rcnt[cur + 1] = 1; 80 } 81 82 int main(){ 83 Pre(); 84 scanf("%d",&T); 85 REP(tt,T){ 86 Read(); 87 88 ll ans = 0; 89 int tot = sumnum; 90 REP(i,cur){ 91 tot -= num[i]; 92 ll C = fac[n] * afac[n - num[i] - tot] % mod * afac[num[i] + tot] % mod; 93 ll tmp = num[i]; 94 FOR(j,1,num[i] - 1){ 95 int top = j; 96 int bot = tot + j - 1; 97 tmp = (tmp + (num[i] - j) * (fac[bot] * afac[bot - top] % mod * afac[top] % mod) % mod) % mod; 98 } 99 ans = (ans + C * cnt[i - 1] % mod * A[i] % mod * tmp % mod * rcnt[i + 1]) % mod; 100 } 101 printf("Case #%d: %I64d\n",tt,ans); 102 } 103 return 0; 104 }