子序列(组合数学)

链接:https://www.nowcoder.com/acm/contest/181/F
来源:牛客网

题目描述

给出一个长度为n的序列,你需要计算出所有长度为k的子序列中,除最大最小数之外所有数的乘积相乘的结果

题解

每个数的贡献容斥一下就好
对于每个数来说,这个数的贡献 = = = 所有含这个数的子序列的个数 − - 含这个数并且这个数是子序列的最小值的个数 − - 含这个数并且这个数是子序列的最大值的个数
不难发现,需要先排序

知识整合

欧拉降幂

a b ≡ a b % φ ( p ) ( m o d    p ) a ^ b \equiv a ^ {b \% \varphi(p)} (mod~~ p) abab%φ(p)(mod  p)
欧拉降幂是幂次对 φ ( p ) \varphi(p) φ(p)取模,而非 p p p
b b b中所有的项的预处理都要对 φ ( p ) \varphi(p) φ(p)取模

杨辉三角

简单的预处理

#include<iostream>
#include<algorithm>
using namespace std;

#define mod 1000000007
typedef long long ll;
const int N = 1e3 + 10;

ll u[N];

ll c[N][N];

ll qpow(ll a, ll b, ll p) {
    
    
	ll res = 1;
	while (b) {
    
    
		if (b & 1) {
    
    
			res = res * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
void init_com(const int n = 1e3) {
    
    
	for (int i = 0; i <= n; i++) {
    
    
		for (int j = 0; j <= i; j++) {
    
    
			if (i == j || j == 0) c[i][j] = 1;
			else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % (mod - 1);
		}
	}
}
int main() {
    
    

	//杨辉三角初始化组合数 
	init_com();
	int T; cin >> T;

	while (T--) {
    
    
		int n, k;
		cin >> n >> k;

		for (int i = 1; i <= n; i++) {
    
    
			cin >> u[i];
		}

		sort(u + 1, u + n + 1);

		ll ans = 1;
		for (int i = 1; i <= n; i++) {
    
    
			//容斥  
			//这个数的贡献 = 含这个数的所有情况 - 以u[i]最大 - u[i]最小
			ll t = c[n - 1][k - 1] - c[i - 1][k - 1] - c[n - i][k - 1];
			//欧拉定理 a^b = a^(b % (p - 1)) 
			//所以幂次需要对(mod - 1)取模
			//由于幂次有组合数,所以预处理组合数时,对(mod - 1)取模就行
			t = (t % (mod - 1) + (mod - 1)) % (mod - 1);
			//答案是对mod取模的
			ans = ans * qpow(u[i], t, mod) % mod;
		}

		cout << ans << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45739057/article/details/109964528