https://ac.nowcoder.com/acm/contest/81596/B
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<queue>
#include<math.h>
#include<string>
#include<map>
#include<functional>
#include<unordered_map>
#include<bitset>
using namespace std;
#define int long long
#define inf 0x3f3f3f3f
#define LL long long
#define maxn 5005
int n, m, mod;
int dp[maxn][maxn];
int c[maxn][maxn];
void init() {
for (int i = 0; i < maxn; i++)
{
c[i][0] = 1;
}
for (int i = 1; i < maxn; i++)
{
for (int j = 1; j <=i; j++)
{
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
c[i][j] %= mod;
}
}
}
LL power(LL a, int n){
LL aws = 1;
while (n){
if (n & 1){
aws *= a, aws %= mod;
}
n >>= 1;
a *= a, a %= mod;
}
return aws;
}
int solve1()
{
LL ans = power(2, n * (m - 1));
dp[0][0] = 1;
for (int i = 0; i <= 5000; i++){
dp[0][i] = dp[i][0] = 1;
}
for (int i = 1; i <= 5000; i++){
for (int j = 1; j <= 5000; j++){
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
dp[i][j] %= mod;
}
}
for (int i = 1; i <= n; i++){
LL res = (power(2, i * (m - 1)) - power((power(2, i) - 1), (m - 1))) % mod + mod;
res %= mod;
res *= power(2, (n - i) * (m - 1));
res %= mod;
res *= dp[i][n - i];
res %= mod;
ans = (ans + res) % mod;
}
return ((power(2, n * m) - ans) % mod + mod) % mod;
}
/*
计算有且只有一个非空子序列 AND 为1
所有的奇数必须都参与到这个序列之中,
也就是说,每一个奇数至少要贡献一个独一无二的零出来,否则这个奇数就可以被排除在最小的非空子序列之外
*/
int dp2[maxn][maxn];
/*
我们令dp2为 有i个数字,对应了j位独一无二的零 的方案的数量
*/
int pow2[maxn*maxn];
int solve2()
{
pow2[0] = 1;
for (int i = 1; i < maxn*maxn; i++)
{
pow2[i] = pow2[i - 1] * 2;
pow2[i] %= mod;
}
int ans = 0;
dp2[0][0] = 1;
for (int i = 1; i <= 5000; i++)
{
for (int j = 1; j <= 5000; j++)
{
dp2[i][j] = i*(dp2[i - 1][j - 1] + dp2[i][j - 1]);
dp2[i][j] %= mod;
}
}
//枚举奇数的数量
for (int i = 1; i <= n; i++)
{
if (i == 1)
{
ans += power(2, (m - 1)*(n - 1))*dp[1][n-1];
ans %= mod;
}
else
{
//枚举独一无二的零的数量
int tmp = 0;
int tmp2 = 1;
for (int k = m - 1; k >= i; k--)
{
tmp += c[m - 1][k]*dp2[i][k] % mod * tmp2 % mod;
tmp %= mod;
tmp2 *= pow2[i] - 1 - i;
tmp2 %= mod;
}
tmp *= pow2[(n - i)*(m - 1)];
tmp %= mod;
tmp *= dp[i][n - i];
tmp %= mod;
ans += tmp;
ans %= mod;
}
}
//cout << ans << '\n';
return ans;
}
signed main(void)
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin >> n >> m >> mod;
init();
int ress = solve1() - solve2();
ress = (ress%mod + mod) % mod;
cout << ress << '\n';
system("pause");
return 0;
}
赛时写的代码带了一个log,后来研究了一下log可以拿掉
主要记录一下这类组合问题的解决方案:
有i个有序的互不相同物品,把他们分给j个不同的箱子,每个箱子至少分一个,所有分配情况的方案数
1,若这些物品时相同的,我们应该采用隔板法来进行分配
2,对于这种情况,我们应该考虑dp
设dp[i][j]为有i个盒子对应了j个物品
dp[i][j]=i*(dp[i-1][j-1]+dp[i][j-1])
对于一个新来的物品,可以放在i个盒子的任意一个盒子中,若这个盒子中已经有了物品,则有物品对应的盒子数量仍为i,若放到的这个盒子中没有物品,则有物品对应的盒子数量为i+1