2021牛客国庆集训派对day6 C Generation I

博客讨论了一个关于数学组合和逆元的编程问题。给定一个三角形矩阵,每行代表一个集合,需要填充特定数字以形成不同集合。题目解析了关键性质和解决方案,涉及子集概念、数字顺序的影响以及乘法逆元的使用。通过扩展欧几里得算法计算逆元,并给出C++代码实现,用于计算可能的集合数量。
摘要由CSDN通过智能技术生成

题目描述:

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.

本文参考了文章牛客网暑期ACM多校训练营(第六场) C (简单排列组合+逆元)_Jiandong-CSDN博客

题意解析:

给定n行,每行具有n个数字,我们需要对这个三角形进行填写数字的操作,对于每一列,我们都需要填写相同的数字,但是这些数字是从1--m,每行作为一个集合,请问我们可以构造出多少个集合的数量

首先我们通过题目可以得出以下几个性质:

1.对于任意第i(i <= n - 1)行,第i行构成的集合必定是第(i + 1)行的子集

2.在最后一行填入数字相同的情况下,填入数字的顺序会影响集合的构成

比如以下两个数字三角形

1                                       1

1    2                                 1   3

1    2    3                           1   3   2

他们所构成的集合是不一样的

因此我们可以知道,最后一行的数字的顺序会影响相应集合的构成

我们对最后一行需要取的数字个数考虑

假设我们需要取K个,然后对这K个相对出现位置进行排序

排序完之后我们需要考虑这些数字第一次出现的位置,则是C(K - 1,N - 1);

对应的式子如下所示

C\binom{k}{m} *A\binom{k}{k} * C\binom{k - 1}{n - 1}

化简之后得到

A\binom{k}{m} * C\binom{k - 1}{n - 1}

然后对于带有除数的式子,进行乘法逆元的操作,接下来就可以解决了

乘法逆元:替代mod 中除以A,用乘以A的逆元来替代

一个数A的逆元(p为质数)数的(p - 2)次方

求逆元更普遍的方式是采用扩展欧几里得的方式

void exgcd(ll a,ll b,ll& d,ll& x,ll& y)
{
    if(!b) { d = a; x = 1; y = 0; }
    else{ exgcd(b, a%b, d, y, x); y -= x*(a/b); }
}

ll inv(ll a, ll p)
{
    ll d, x, y;
    exgcd(a, p, d, x, y);
    return d == 1 ? (x+p)%p : -1;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const ll mod = 998244353;
ll n,m;
int T;
ll inv[maxn];
ll quickpow(ll a,ll b)
{
	if(b == 0) return 1;
	if(b & 1) return (quickpow(a,b - 1) % mod) * (a % mod) % mod;
	else
	{
		ll tmp = quickpow(a,b >> 1) % mod;
		return (tmp % mod) * (tmp % mod) % mod;
	}
}
void pre()
{
	for(int i = 1;i <= maxn - 8;++i)
	{
		inv[i] = (quickpow(i,mod - 2) + mod) % mod;
	}
}
int main()
{
	scanf("%d",&T);
	pre();
	for(int u = 1;u <= T;++u)
	{
		ll ans = 0;
		scanf("%lld %lld",&n,&m);
		ll end = min(n,m);
		n %= mod;
		m %= mod;
		ll mul = m;
		ll sum = 1;
        ans = m;
       // cout << inv[1] <<"\n";
		for(ll i = 1;i < end;++i)
		{
			mul = mul % mod * ((m - i) % mod) % mod;
			sum = sum % mod * (inv[i]) % mod * ((n - i) % mod) % mod;
			ans += (mul % mod) * (sum % mod) % mod;
		}
		ans = (ans + mod) % mod;
		printf("Case #%d: %lld\n",u,ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值