2026.8.13

A

给定正整数 \(n,k\),求将 \(n\) 个相同弹珠划分到 \(k\) 个相同盒子且每个盒子非空的方案数,答案对 \(998244353\) 取模。数据范围:\(1 \le k \le n \le 5000\)

似乎是一个经典的 DP 但是我好像真没见过。

考虑 \(dp(i,j)\) 表示 \(i\) 个数分到 \(j\) 个盒子的方案数。因为必须要保证数列的非增(非降)性,保证不重复,所以可以考虑以下两种操作:一种是在最前面加一个 \(1\),一种是所有数都 \(+1\)

  • 至少有一个是 \(1\),那就把这个 \(1\) 拿走,剩下 \(i-1\) 个分成 \(j-1\) 组(相当于在前面补 \(1\)
  • 没有一个 \(1\),那就全体 \(-1\),剩下 \(i-j\) 个分成 \(j\) 组(相当于集体 \(+1\)

然后状态转移:

\[dp[i][j]=dp[i-1][j-1]+dp[i-j][j] \]

发现如果用插板法直接 \(\binom{n-1}{k-1}\) 的话,会有重复的,而且每种组合的重复数还不一样(比如 \((1,1,2)\) 有三种,而 \((1,1,1)\) 只有一种),所以不能这样,然后赛时只会这样然后又光荣爆 T1 了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=5007;
constexpr int mod=998244353;
int n,k;
ll dp[N][N];
int main()
{
//	freopen("neuvillette.in","r",stdin);
//	freopen("neuvillette.out","w",stdout);
	cin.tie(0)->sync_with_stdio(0);
	cin>>n>>k;
	dp[0][0]=1; dp[1][1]=1;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i;j++) dp[i][j]=(dp[i-1][j-1]+dp[i-j][j])%mod;
	cout<<dp[n][k]%mod;
	cout.flush();
	return 0;
}
/*
dp[i][j]表示i个苹果放进j个盘子的方案数
至少有一个是1,那就把这个1拿走,剩下i-1个分成j-1组(相当于在前面补1)
没有一个1,那就全体-1,剩下i-j个分成j组(相当于集体+1) 
dp[i][j]=dp[i-1][j-1]+dp[i-j][j]
*/

B

给定正整数 \(N,K\),求将 \(N\) 个不同的球放入 \(3\) 个相同袋子(袋子允许为空)的方案数 \(M\),输出 \(M \bmod K\)。数据范围:\(10 \le N \le 10^9,K \le 10^5\)

题目居然把 \(n=1 \sim 10\) 都打好了,这不是直接猜结论?

使用 Desmos 描点,然后使用高一必修一学过的函数拟合方法猜。首先发现是指数级增长,所以直接猜一个指数型函数;然后又是 \(3\) 个袋子,所以是 \(3^x\),然后不断调参,发现是 \(\dfrac12 (3^x-1)\) 然后发现点似乎集体偏移了,然后变成 $\dfrac{1}{2} \left(3^{x-1}+1\right) $ 就直接过了。

怎么处理这个傻逼任意模数?首先答案 \(A=\dfrac{1}{2} \left(3^{x-1}+1\right)\) 是偶数,然后有关系式:

\[\left(\frac{A}{2}\right) \bmod k = \frac{A \bmod 2k}{2} \]

\(A = 2k \cdot q + r\),其中余数 \(r\) 满足 \(0 \leq r < 2k\)。因为 \(2k\) 是偶数,且 \(A\) 是偶数,所以余数 \(r\) 也必然是偶数。令 \(r = 2t\)。则 \(A / 2 = k \cdot q + t\)。因此 \((A / 2) \bmod k = t\)。而代码右边 \((A \bmod 2k) / 2 = r / 2 = t\)。两边相等,定理得证。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=-1;
ll mod;
ll ksm(ll a,ll b)
{
	ll s=1;
	while(b)
	{
		if(b&1) s=s*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return s;
}
int main()
{
//	freopen("neuvillette.in","r",stdin);
//	freopen("neuvillette.out","w",stdout);
	cin.tie(0)->sync_with_stdio(0);
	ll n;
	cin>>n>>mod; mod*=2;
	ll tmp=(ksm(3,n-1)%mod+1);
	cout<<tmp/2;
	cout.flush();
	return 0;
}
/*
使用desmos拟合可知解析式为\frac{1}{2}\left(3^{\left(x-1\right)}+1\right) 
2在任意模数下的逆元? 
A mod m=r
(2A) mod (2m)=2r

(2a) mod 2m=2(a mod m)
a mod m=1/2 (a mod 2m)
*/
posted @ 2026-08-13 21:28  wwwidk1234  阅读(14)  评论(0)    收藏  举报