[luogu p4640] [BJWC2008]王之财宝
P4640 [BJWC2008]王之财宝 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
球盒问题,轻车熟路。
首先考虑简化问题到
此时再考虑逐渐放宽问题一直到题目的样子,首先先将不能不放球的限制砍掉。
其实这个时候也就相当于没放的球自己独立进一个盒子,也就相当于盒子多了一个,
现在再加入盒子容量的限制。观察到
再考虑容斥原理,答案等于至少0个盒子超载方案数(上面刚刚求的)-至少任意1个盒子超载总方案数+至少任意2个盒子超载总方案数-至少任意3个盒子超载总方案数……
接下来举例说明:
总共有三个空间有限的盒子,至少
考虑至少让
-
将盒子
塞超,也就是塞满+1(容量+1),剩下的球在 个盒子里随便选着放,方案数: ; -
将盒子
塞超,剩下的球在 个盒子里随便选着放,方案数: ; -
将
塞超,…… 。
第一个盒子的所有超载情况方案数就是上面三个结果的和,根据容斥的式子,总共需要减去这个和。
接下来,要至少让
首先把
然后把
最后把
那么至少让
然后再让至少(总共就3个其实已经不是至少了)
就是所有都塞超:
根据容斥的式子总共需要减去这个东西。
最后容斥统计,可以得到结果是这样一个东西:
到这里思路也就讲完了,但是还有一个东西:
考虑到上述这个式子就是很多的组合数相加相减,同时观察到这些组合数一一对应了所有
实际上我们只需要暴力枚举
这部分的代码:
for (int i = 0; i < (1 << t); ++i) {
int cnt = 0, k = m;
for (int j = 1; j <= t; ++j)
if (i & (1 << j - 1)) {
++cnt;
k -= b[j] + 1;
}
int g = lucas(n + k, n);
(ans += (cnt & 1) ? p - g : g) %= p;
}
对于组合数,考虑到
/*
* @Author: crab-in-the-northeast
* @Date: 2022-05-04 10:55:29
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2022-05-04 11:12:58
*/
#include <iostream>
#include <cstdio>
#define int long long
inline int read() {
int x = 0;
bool flag = true;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
flag = false;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
if(flag) return x;
return ~(x - 1);
}
const int maxp = 1e5 + 5;
const int maxt = 20;
int b[maxt];
int n, m, t, p;
inline int pow(int a, int b = p - 2LL) {
int s = 1;
for (; b; b >>= 1, (a *= a) %= p)
if (b & 1)
(s *= a) %= p;
return s;
}
int fac[maxp], inv[maxp];
inline int C(int n, int m) {
if (n < m)
return 0;
return fac[n] * inv[n - m] % p * inv[m] % p;
}
inline int lucas(int n, int m) {
int ans = 1;
for (; m; n /= p, m /= p)
(ans *= C(n % p, m % p)) %= p;
return ans;
}
signed main() {
n = read();
t = read();
m = read();
p = read();
for (int i = 1; i <= t; ++i)
b[i] = read();
fac[0] = inv[0] = 1;
for (int i = 1; i < p; ++i)
fac[i] = fac[i - 1] * i % p;
inv[p - 1] = pow(fac[p - 1]);
for (int i = p - 2; i; --i)
inv[i] = inv[i + 1] * (i + 1) % p;
int ans = 0;
for (int i = 0; i < (1 << t); ++i) {
int cnt = 0, k = m;
for (int j = 1; j <= t; ++j)
if (i & (1 << j - 1)) {
++cnt;
k -= b[j] + 1;
}
int g = lucas(n + k, n);
(ans += (cnt & 1) ? p - g : g) %= p;
}
printf("%lld\n", ans);
return 0;
}
推荐!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】