【0823 B组】模拟测试 mit 题解

发布时间 2023-12-19 11:48:51作者: Creeper_l

原题传送门:mit

前言

这道题是今天模拟赛T1,赛时只有 \(60\) 分。还有一位巨佬这道题保龄了

题意

给定一个正整数 \(n\),将 \(n\) 拆分成 \(k\) 个数之和。(\(k\) 为任意正整数)

求:\(k\) 个数的乘积最大是多少?乘积的期望是多少?

思路

首先看第一个问题。易证,要让乘积最大,必须将 \(n\) 分成若干个 \(3\)。当然如果最后剩了 \(1\) 的话就换成两个 \(2\)

再看第二个问题,赛时想了好久都没有想出来。首先我们可以想到 \(O(n^{2})\)\(dp\)

\(f_{i}\) 表示正整数为 \(i\) 的时候的期望。假设当前这个数拆成了 \(k\),则有:

\(f_{i}=\frac{1}{i} \times \sum_{k=1}^{i} f[i-k]\)

然后考虑如何优化。可以发现式子后面的 \(\sum_{k=1}^{i} f[i-k]\) 可以用前缀和优化。

于是设 \(h_{i} = \sum_{k = 1}^{i}f_{k}\),设 \(g_{i}=\sum_{k=1}^{i}f_{k} \times(i-k+1)\),则有:

\(f_{i}=g_{i-1} \times \frac{1}{i}\)

\(h_{i}=h_{i-1} +f_{i}\)

\(g_{i}=g_{i-1}+h_{i}\)

这样就可以 \(O(n)\) 算出答案了,答案就是 \(f_{n}\)。除以 \(i\) 的时候改为乘上 \(i\) 的逆元就行了。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x3f3f3f3f
#define ls id << 1
#define rs id << 1 | 1
#define re register 
typedef pair <int,int> pii;
const int MAXN = 5e5 + 10;
const int MAXM = 20;
int m,n,inv[MAXN],MAX = 1,mod; 
int f[MAXN],g[MAXN],h[MAXN];
signed main()
{
	cin >> n >> mod;m = n;
	while(m >= 5){MAX = MAX * 3 % mod;m -= 3;}
	MAX = MAX * m % mod;
	inv[0] = inv[1] = 1;
	for(int i = 2;i <= n;i++) inv[i] = inv[mod % i] * (mod - mod / i) % mod;
	f[0] = g[0] = h[0] = 1;
	for(int i = 1;i <= n;i++)
	{
		f[i] = g[i - 1] * inv[i] % mod;
		h[i] = (h[i - 1] + f[i]) % mod;
		g[i] = (g[i - 1] + h[i]) % mod;
		
	}
	cout << MAX << endl << f[n];
	return 0;
}