牛客网暑期ACM多校训练营(第六场) C.Generation I (思维+逆元+组合数学)

题目链接

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

Oak is given N empty and non-repeatable sets which are numbered from 1 to N.


Now Oak is going to do N operations. In the i-th operation, he will insert an integer x between 1 and M to every set indexed between i and N.


Oak wonders how many different results he can make after the N operations. Two results are different if and only if there exists a set in one result different from the set with the same index in another result.

Please help Oak calculate the answer. As the answer can be extremely large, output it modulo 998244353.

输入描述:

The input starts with one line containing exactly one integer T which is the number of test cases. (1 ≤ T ≤ 20)

Each test case contains one line with two integers N and M indicating the number of sets and the range of integers. (1 ≤ N ≤ 1018, 1 ≤ M ≤ 1018, )

输出描述:

For each test case, output "Case #x: y" in one line (without quotes), where x is the test case number (starting from 1) and y is the number of different results modulo 998244353.

示例1

输入

2
2 2
3 4

输出

Case #1: 4
Case #2: 52

题意:T组测试样例,每组样例给出N和M,表示有N个集合(初始为空,且集合元素不重复),M种不同的元素,最后N次操作,第i次可以选择一种编号(1~M)的元素,然后在第i~N个集合中都放一个该元素,问你一共可以产生多少种不同结果?

题解:换个思考方式(大佬教的):把1~N当做楼梯,对于每次操作,若是取一种前面都没有取过的颜色(之后都改用颜色,方便看图理解),那么该格楼梯比上一格高一阶,若是取之前去过的任意一种颜色,那么该格楼梯和上一格高度一样如下图:

                        

     上图就和题目的说明一样,应该很容易理解了吧,对于每层的上升就是选择了一种前面没有选择过的颜色,若是选择的是前面选过的任意一种颜色,该处的层数不会上升,而是平的~

      那么下面来计算一下结果:对于给出N个格子,M种颜色,构造这个楼梯最多只能选择min(N,M)种颜色,那么假设当前选择了K种颜色(由于最少选择一种颜色,因此K的范围是1~min(N,M)),那么有C_{M}^{K}\textrm{}种颜色可以选择,且对于这些颜色的上色顺序可以是随意的,那么就有C_{M}^{K}\textrm{A_{K}^{K}\textrm{}}种结果,还有就是对于我们在1~N中选择新颜色投放位置(即阶梯的上升部分),由于第一层一定要放一种颜色,所以剩下K-1种颜色,和N-1个可以选择的投放位置,那么有C_{N-1}^{K-1}\textrm{}种结果,乘起来就得到了选K种颜色的情况数:C_{M}^{K}\textrm{A_{K}^{K}\textrm{}}C_{N-1}^{K-1}\textrm{}.

      所以可以得到总的情况数为:\sum C_{M}^{K}\textrm{A_{K}^{K}\textrm{}}C_{N-1}^{K-1}\textrm{}      K的范围是(1~min(N,M));

      剩下的一点逆元的操作了,我是先对上式子进行化简后求的,且注意预处理出1e6内的逆元表(貌似不弄会TLE的)~

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<queue>
#include<queue>
using namespace std;
#define ll long long
const int maxn = 1e6 + 100;
const ll mod = 998244353;
ll inv[maxn];
void init() {  //O(n)的时间内预处理出1~1e6的逆元表
	inv[0] = 0;
	inv[1] = 1;
	for (ll i = 2; i<=1e6; i++) {
		inv[i] = (mod - mod / i)*inv[mod%i] % mod;
	}
}
int main()
{
	init();
	int T;
	scanf("%d", &T);
	for (int t = 1; t <= T; t++) {
		ll n, m;
		scanf("%lld%lld", &n, &m);
		n %= mod, m %= mod;
		ll MIN = min(n, m);
		ll Cm = m;
		ll Cn = 1;
		ll S = 1;
		ll ans = m;//k=1时
		for (ll k = 2; k <= MIN; k++) {
			Cm = ((Cm*(m - k + 1) % mod)) % mod;
			Cn = ((Cn*(n - k + 1) % mod)*inv[k - 1]) %mod;
			//S = (S*(k-1)) % mod;
			ans = (ans + Cm*Cn%mod) % mod;
		}
		printf("Case #%d: %lld\n", t, ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/81430568
今日推荐