[十二省联考2019]皮配 - 背包、dp、计数
Description
简化版题面:
有 \(c\) 个豆荚,共 \(n\) 颗豆子,每颗豆子都有自己的重量,现在需要将给豆子设定为(黄色/绿色,圆粒/皱粒),要求满足以下条件:
-
给定这四种性状的阀值 \(C0,C1,D0,D1\),要求为这种性状的豆子重量和不能超过该阀值。
-
与此同时,这 \(n\) 颗豆子中存在 \(k\) 颗顽皮豆,顽皮豆都有自己的想法,比如拒绝成为(黄圆/黄皱/绿圆/绿皱)。
-
同一个豆荚里的豆子必须同时为黄色或同时为绿色。
求有多少种给豆子设定的方案,答案对 \(998244353\) 取模。
Solution
本题只考查了联赛级别的知识点背包,很普及组吧。好评!XD
算法一 暴力
纯暴力。
设 \(f_{i,0/1,x,y,z}\) 为第 \(i\) 个学校选择的是红/蓝阵营,前三位导师分别有 \(x,y,z\) 个选手的方案数,然后枚举每个学校进行转移即可。复杂度 \(O(nM^3)\),预计得分 \(30pts+\)。
其实并不需要设三维的 \(\text{dp}\),只需设状态 \(f_{i,0/1,x,y}\) 表示第 \(i\) 个学校选择的是红/蓝阵营,选择蓝阵营的有 \(x\) 人,鸭派系的有 \(y\) 人,因为一个选手必定属于其中一个阵营/派系,复杂度 \(O(nM^2)\),预计得分 \(50pts\)。
算法二 \(k=0\)
考虑 \(k=0\) 的情况。
此时任何学校都没有导师的限制,可以发现先确定阵营再确定派系,与同时确定阵营与派系(即直接确定某位导师)所得到的方案数是等价的。(类似于生物里的两对相对性状?)
设 \(f_{i,1/0,j}\) 为第 \(i\) 个学校选择红/蓝阵营,且前 \(i\) 个学校选择蓝阵营的有 \(j\) 个人的方案数,\(g_{i,j}\) 为前 \(i\) 个学校选择鸭派系的有 \(j\) 个人的方案数,那么分别计算出来后,进行合并,即总方案数为
其实本质上就是背包及背包合并。
复杂度 \(O(nM)\),结合前面的算法可以拿到 \(70pts\)。
随便写写暴力就70pts了
算法三 正解
继续沿着算法二的思路进行思考,拓展到更为一般的情况。
还是设 \(f\) 和 \(g\) 分别表示阵营和派系的方案数,考虑如何处理学校对导师的限制。
下面对于阵营的划分,都基于城市为单位进行考虑;对于派系的划分,都基于学校为单位进行考虑(若以城市为单位进行 \(\text{dp}\),一个没有限制的学校因为同城市其它学校的偏好被 \(\text{dp}\) 到会感觉很不爽 \(\text{2333}\))。
一个城市有限制,当且仅当城市内的学校有限制。
所以对于那些没有限制的城市和学校,我们还是可以仿照算法二,直接 \(\text{dp}\) 出 \(f\) 和 \(g\)。
而对于那些有限制的学校/城市,由于 \(k\) 很小,只有 \(30\),我们可以仿照 \(50pts\) 的算法,暴力 \(\text{dp}\) 出方案数,记为 \(F\)。
设 \(F_{i,0/1,j,k}\) 表示前 \(i\) 个有限制的学校,第 \(i\) 个学校加入了红/蓝阵营,加入红阵营的有 \(j\) 人,这些学校所属城市加入了鸭派系的有 \(k\) 人的方案数。
最后,进行合并,只要枚举有限制的学校的派系、阵营人数,然后用 \(f\) 去匹配无限制城市的阵营人数的上下界,用 \(g\) 去匹配无限制的学校的派系人数的上下界,再全部乘起来就好了。
简单的背包,吐了XDXDXD
Code
Talk is cheap. Show me the code.
Tips:
- 滚动数组每轮记得清零!
- 不要用
memset
,贼慢!
#include <bits/stdc++.h>
using namespace std;
int ty() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef long long ll;
const int _ = 2500 + 10;
const int mod = 998244353;
int N, C, K, C0, C1, D0, D1, sum;
int f[5005], g[5005], F[2][2][5005][5005];
struct school {
int city, num, prf;
bool operator<(const school &x) const {
if ((prf != 0) ^ (x.prf != 0)) return (prf != 0) > (x.prf != 0);
return city < x.city;
}
} scl[_];
struct city { int num, hate; } ct[_];
void Inc(int &x, const int &y) { x = x + y < mod ? x + y : x + y - mod; }
void clear() {
sum = 0;
// memset(f, 0, sizeof(f));
// memset(g, 0, sizeof(g));
// memset(F, 0, sizeof(F));
memset(ct, 0, sizeof(ct));
memset(scl, 0, sizeof(scl));
}
void init() {
N = ty(), C = ty();
C0 = ty(), C1 = ty(), D0 = ty(), D1 = ty();
for (int i = 1; i <= N; ++i) {
scl[i].city = ty(), scl[i].num = ty();
sum += scl[i].num;
}
K = ty();
for (int i = 1; i <= K; ++i) {
int x = ty();
scl[x].prf = ty() + 1;
}
sort(scl + 1, scl + N + 1);
for (int i = 1; i <= N; ++i) {
ct[scl[i].city].num += scl[i].num;
if (scl[i].prf) ct[scl[i].city].hate = true;
}
}
ll calc1(int l, int r) {
return l > r ? 0 : (l <= 0 ? g[r] : (g[r] - g[l - 1] + mod) % mod);
}
ll calc2(int l, int r) {
return l > r ? 0 : (l <= 0 ? f[r] : (f[r] - f[l - 1] + mod) % mod);
}
void dp() {
f[0] = 1;
for (int i = 1; i <= C; ++i)
if (!ct[i].hate && ct[i].num)
for (int j = C0; j >= ct[i].num; --j) Inc(f[j], f[j - ct[i].num]);
for (int i = 1; i <= C0; ++i) f[i] = (f[i] + f[i - 1]) % mod;
g[0] = 1;
for (int i = 1; i <= N; ++i)
if (!scl[i].prf)
for (int j = D0; j >= scl[i].num; --j) Inc(g[j], g[j - scl[i].num]);
for (int i = 1; i <= D0; ++i) g[i] = (g[i] + g[i - 1]) % mod;
F[0][0][0][0] = 1;
int sum1 = 0, nw = 0;
for (int i = 1; i <= K; ++i) {
sum1 += scl[i].num;
for (int x = 0; x <= C0; ++x) {
for (int y = 0; y <= sum1; ++y) {
if (scl[i].prf != 1) {
if (scl[i].city != scl[i - 1].city) {
if (x >= ct[scl[i].city].num && y >= scl[i].num) {
Inc(F[nw ^ 1][0][x][y],
F[nw][1][x - ct[scl[i].city].num][y - scl[i].num]);
Inc(F[nw ^ 1][0][x][y],
F[nw][0][x - ct[scl[i].city].num][y - scl[i].num]);
}
} else {
if (y >= scl[i].num)
Inc(F[nw ^ 1][0][x][y], F[nw][0][x][y - scl[i].num]);
}
}
if (scl[i].prf != 2) {
if (scl[i].city != scl[i - 1].city) {
if (x >= ct[scl[i].city].num) {
Inc(F[nw ^ 1][0][x][y], F[nw][1][x - ct[scl[i].city].num][y]);
Inc(F[nw ^ 1][0][x][y], F[nw][0][x - ct[scl[i].city].num][y]);
}
} else {
Inc(F[nw ^ 1][0][x][y], F[nw][0][x][y]);
}
}
if (scl[i].prf != 3) {
if (scl[i].city != scl[i - 1].city) {
if (y >= scl[i].num) {
Inc(F[nw ^ 1][1][x][y], F[nw][0][x][y - scl[i].num]);
Inc(F[nw ^ 1][1][x][y], F[nw][1][x][y - scl[i].num]);
}
} else {
if (y >= scl[i].num)
Inc(F[nw ^ 1][1][x][y], F[nw][1][x][y - scl[i].num]);
}
}
if (scl[i].prf != 4) {
if (scl[i].city != scl[i - 1].city) {
Inc(F[nw ^ 1][1][x][y], F[nw][0][x][y]);
Inc(F[nw ^ 1][1][x][y], F[nw][1][x][y]);
} else {
Inc(F[nw ^ 1][1][x][y], F[nw][1][x][y]);
}
}
}
}
for (int x = 0; x <= C0; ++x)
for (int y = 0; y <= sum1; ++y) F[nw][0][x][y] = F[nw][1][x][y] = 0;
nw ^= 1;
}
int ans = 0;
for (int i = 0; i <= C0; ++i) {
for (int j = 0; j <= sum1; ++j) {
ll A = (F[nw][0][i][j] + F[nw][1][i][j]) % mod;
ans = (1ll * ans + A * calc1(sum - D1 - j, D0 - j) % mod * calc2(sum - C1 - i, C0 - i) % mod) % mod;
}
}
printf("%d\n", ans);
for (int i = 0; i <= C0; ++i) {
f[i] = 0;
for (int j = 0; j <= sum1; ++j)
F[0][0][i][j] = F[0][1][i][j] = F[1][0][i][j] = F[1][1][i][j] = 0;
}
for (int i = 0; i <= D0; ++i) g[i] = 0;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("match.in", "r", stdin);
freopen("match.out", "w", stdout);
#endif
int T = ty();
while (T--) {
clear();
init();
dp();
}
return 0;
}