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 }

 

  

转载于:https://www.cnblogs.com/naturepengchen/articles/4231741.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值