1. 程式人生 > >F-子序列(組合數,打表,擴充套件尤拉,容斥)

F-子序列(組合數,打表,擴充套件尤拉,容斥)

題目連結

題目描述

給出一個長度為n的序列,你需要計算出所有長度為k的子序列中,除最大最小數之外所有數的乘積相乘的結果

輸入描述:

第一行一個整數T,表示資料組數。
對於每組資料,第一行兩個整數N,k,含義如題所示

接下來一行N個整數,表示給出的序列

保證序列內的數互不相同

輸出描述:

對於每組資料,輸出一個整數表示答案,對取模
每組資料之間以換行分割

輸入

3
4 3 
5 3 1 4
5 4
3 7 5 2 1
10 3 
100 1020 2050 102 12 235 4 57 32135 54354 

輸出

144
81000
521918013

說明

第一組資料解釋
所有長度為3的子序列為(5,3,1)(5,3,4)(3,1,4)(5,1,4)
最終答案為 3*4*3*4 = 144 

AC

  • 容斥求出每個數利用的個數,總的出現次數-這個數作為最小值出現-這個數作為最大值出現

    a [ i ] : C ( n k
    1 ) C ( n i k 1 ) C ( i 1 k 1 )

  • 求組合數可以打表,組合數的數值很大而且結果要作為指數計算,這裡就要用到擴充套件尤拉

    a b = a b % ϕ ( p )

#include <iostream>
#include <stdio.h>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 1015
#define ll  long long
using namespace std;
ll c[N][N], mod = 1e9 + 7, a[N];
ll C(int n, int m) {
    if (m > n || n <= 0)    
        return 0;
    else
        return c[n][m];
} 
ll quick_pow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) {
            ans = ans * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("in.txt", "r", stdin);
#endif
    // 組合數打表 
    c[0][0] = 1;
    for (int i = 1; i < N; ++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 - 1);
        }
    }
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, k;
        scanf("%d%d", &n, &k);
        for (int i = 0; i < n; ++i) {
            scanf("%lld", &a[i]); 
        }
        sort(a, a + n);
        ll ans = 1;
        for (int i = 0; i < n; ++i) {
            ll mi = c[n - 1][k - 1] - C(n - i - 1, k - 1) - C(i, k - 1);
            // 擴充套件尤拉 
            mi = (mi + mod - 1) % (mod - 1);
            ans = quick_pow(a[i], mi) * ans % mod;
        }
        printf("%lld\n", ans);
    } 
    return 0;
}