牛客网暑期ACM多校训练营(第六场)C Generation I

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

时间限制: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.

输入

2
2 2
3 4

输出

Case #1: 4
Case #2: 52

题意:n个集合,m个数字,n次操作,第i次操作从m个数字中选择一个插入i~n的集合中,n次操作结束后,问有几种不同的集合情况。集合数字不重复,任意一个对应集合不同,则为不同情况

思路:

首先明确,当前位置插入的数字已经在前面插入过,则这个数字的插入是没有意义的,不会改变这一情况(如{1}{1,2}{1,2}{1,2}第三次操作插入1或者2之后集合都不会改变,还是{1}{1,2}{1,2}{1,2}),那么我们考虑m个数字中取i个数字,插入到n个集合中,只考虑每个数字第一次出现的位置(其他位置无论插入什么,只要不改变每个数字第一次出现的位置,都不会改变集合最终状态)。比如n为5  (1,2,3,X,4),X这个位置的操作,插入1 2 3 对集合最终状态都没有影响。

那么就转化为n个可选位置中选择i个位置,而数字顺序改变情况改变,所以就是n个位置取i个位置,这i个位置放入m个数字中取i个数字的全排列,即C_{n}^{i}*A_{m}^{i},答案就是i从1到min(n,m),因为无论是数字取完了还是位置不够了,都要停止。

但是如果这么做,我们会发现答案是不对的,这是因为,n个位置中,第一个位置必须有数字,而上述公式,没有去掉第一个位置为空的情况。所以我们试想,如果只有一个位置,那么情况就是m种,接下来,n-1个位置的数字插入就可以按照上面得到的式子计算了,即答案为m*\sum_{i=0}^{min(n-1,m-1)} C_{n-1}^{i}*A_{m-1}^{i}

好了公式出来了,但是直接用板子求组合数是不行的,因为n,m的范围是10的18次,所以要直接手动求,因为n-1是不变的,只有i在变,所以按照组合数公式直接乘一个数字除一个数字(当然除用逆元求),即可

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
using namespace std;
typedef long long ll;

const ll mod=998244353;

ll mo(ll a,ll pp){
    if(a>=0&&a<pp)return a;
    a%=pp;
    if(a<0)a+=pp;
    return a;
}
ll powmod(ll a,ll b,ll pp){
    ll ans=1;
    for(;b;b>>=1,a=mo(a*a,pp)){
        if(b&1)ans=mo(ans*a,pp);
    }
    return ans;
}
ll b[1000006],inv[1000006];
void init(){
    b[0]=1;
    for(int i=1;i<=1000000;i++)b[i]=b[i-1]*i%mod;
    for(int i=0;i<=1000000;i++)inv[i]=powmod(i,mod-2,mod);
}

int main(){
	int t;
	scanf("%d",&t);
	int c=1;
	init();
	while(t--){
		ll n,m;
		scanf("%lld%lld",&n,&m);
		ll ans=0;
		ll x=min(n,m);
		ll cn=1;
		ll cm=1;
		for(ll i=0;i<x;i++){
			ans=(ans+cm*cn%mod)%mod;
			cn=(n-i-1)%mod*cn%mod*inv[i+1]%mod;
			cm=(m-i-1)%mod*cm%mod;
		}
		printf("Case #%d: %lld\n",c++,m%mod*ans%mod);		
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yz467796454/article/details/81414590