题意概要:有 \(c\) 个豆荚,共 \(n\) 颗豆子,每颗豆子都有本身的重量,如今须要将给豆子设定为 (黄色/绿色,圆粒/皱粒),要求知足如下条件:git
求有多少种给豆子设定的方案,对 \(998244353\) 取模数组
\(n,c\leq 10^3,k\leq 30\)spa
设 \(M=\max\{C_0,C_1,D_0,D_1\}\),\(M\leq 2500\)线程
豆子重量不超过 \(\min\{M,10\}\)code
首先有一个 \(O(nM^3)\) 的暴力:设定三维——“黄圆”、“黄皱”和“绿圆”的豆子重量和,枚举每一颗豆子去更新。酱紫就有 \(30pts+\)ci
其次有一个 \(O(nM^2)\) 的暴力:设定两维——“黄色”和“圆粒”的重量和。酱紫有 \(50pts\)get
再者考虑 \(k=0\):全部豆子都没有限制,考虑将豆子进行划分。发现不管是先划分 黄/绿 仍是先划分 圆/皱 都对结果没有影响,对应的这二者能够分开计算最后相乘:it
设 \(f[i]\) 表示圆粒重量和为 \(i\) 的方案数,\(g[i]\) 表示圆粒重量和为 \(i\) 的方案数,这两个数组能够 \(O(nM)\) 背包求得,而后答案就为(设全部豆子重量和为 \(S\)):io
\[\sum_{i=S-C_1}^{C_0}\sum_{j=S-D_1}^{D_0}f[i]g[j]\]
作到这再算上前边的就有 \(70pts\)
如今考虑将顽皮豆加入 肯德基豪华午饭 考虑范畴
称这些顽皮豆为“有毒”的豆子,对应的豆荚必定为“有毒”的豆荚
(在阅读下面内容时,请时刻牢记:对于 黄/绿 的划分是以“豆荚”为单位的;对于 圆/皱 的划分是以“豆子”为单位的)
那么依照前一档部分分,这两个是能够乘起来的
不难发现,求完 \(f\) 后就不用再管“无毒”豆荚的 黄/绿,求完 \(g\) 后就不用再管“无毒”豆子的 圆/皱,余下须要考虑的就只有“有毒”豆子的 黄/绿、圆/皱 了
因为“有毒”的豆子很少,只有 \(30\) 个,能够对这一部分考虑采用 \(50pts\) 那一档的暴力 Dp,dp 数组设为 \(F\)(注意 黄/绿 维度用的是整个豆荚的重量,圆/皱 维度用的是单个豆子的重量)
最后只要枚举“有毒”豆子的两个性状的重量 \(F\),会获得一个 黄/绿 划分和一个 圆/皱 划分,考虑用 \(f\) 去匹配 黄/绿(这样全部“有毒”与“无毒”豆荚的 黄/绿 就处理完了),用 \(g\) 去匹配 圆/皱 划分(这样全部“有毒”与“无毒”的豆子的 圆/皱 就处理完了),乘起来便可
计算时间复杂度(空间反正是 \(O(M^2)\) 的):
总时间复杂度为 \(O((c+n)M+k^2sM)\)
另说,这题只考察了联赛级别的知识点——背包,包括:分部背包、双线程背包、背包合并……
#include <bits/stdc++.h> using namespace std; typedef long long ll; inline void read(int&x){ char ch=getchar();x=0;while(!isdigit(ch))ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); } const int p = 998244353; inline int qm(const int x) {return x < p ? x : x - p;} inline void pls(int&x, const int&y) {x = x+y < p ? x+y : x+y-p;} const int N = 1010, M = 2503; bool city_hate[N]; int city_sum[N], hate[N], b[N], s[N]; int f[M], pre_f[M], F[M][M]; int g[M], pre_g[M], G[M][M]; int n, c; void work() { read(n), read(c); int C0, C1, D0, D1, ALL = 0; for(int i=1;i<=c;++i) city_hate[i] = false, city_sum[i] = 0; read(C0), read(C1), read(D0), read(D1); for(int i=1;i<=n;++i) read(b[i]), read(s[i]), city_sum[b[i]] += s[i], hate[i] = -1, ALL += s[i]; int K, x; read(K); while(K--) read(x), read(hate[x]), city_hate[b[x]] = true; memset(f, 0, sizeof f), pre_f[0] = f[0] = 1; for(int i=1;i<=c;++i) if(!city_hate[i] and city_sum[i]) for(int j = C0, ts = city_sum[i]; j >= ts; -- j) pls(f[j], f[j-ts]); for(int i=1;i<=C0;++i) pre_f[i] = qm(pre_f[i-1] + f[i]); memset(g, 0, sizeof g), pre_g[0] = g[0] = 1; for(int i=1;i<=n;++i) if(-1 == hate[i]) for(int j = D0, ts = s[i]; j >= ts; -- j) pls(g[j], g[j-ts]); for(int i=1;i<=D0;++i) pre_g[i] = qm(pre_g[i-1] + g[i]); int Cs = 0, Ss = 0; memset(F, 0, sizeof F), F[0][0] = 1; memset(G, 0, sizeof G); for(int ct = 1; ct <= c; ++ ct) if(city_hate[ct]) { Cs += city_sum[ct], Cs = min(Cs, C0); for(int i=0;i<=Cs;++i) for(int j=0;j<=Ss;++j) G[i][j] = F[i][j]; for(int a=1;a<=n;++a) if(b[a] == ct and ~hate[a]) { const int t = s[a]; Ss += t, Ss = min(Ss, D0); if(hate[a] == 1) for(int i=0;i<=Cs;++i) { for(int j=Ss;j>=t;--j) F[i][j] = F[i][j-t]; for(int j=t-1;~j;--j) F[i][j] = 0; } if(hate[a] >= 2) for(int i=0;i<=Cs;++i) for(int j=Ss;j>=t;--j) pls(F[i][j], F[i][j-t]); if(hate[a] == 3) for(int i=0;i<=Cs;++i) { for(int j=Ss;j>=t;--j) G[i][j] = G[i][j-t]; for(int j=t-1;~j;--j) G[i][j] = 0; } if(hate[a] <= 1) for(int i=0;i<=Cs;++i) for(int j=Ss;j>=t;--j) pls(G[i][j], G[i][j-t]); } for(int j=0,t=city_sum[ct];j<=Ss;++j) { for(int i=Cs;i>=t;--i) F[i][j] = F[i-t][j]; for(int i=t-1;~i;--i) F[i][j] = 0; } for(int i=0;i<=Cs;++i) for(int j=0;j<=Ss;++j) pls(F[i][j], G[i][j]); } int res = 0; for(int i=0;i<=Cs;++i) for(int j=0;j<=Ss;++j) { int l1 = max(0, ALL - C1 - i), r1 = C0 - i; if(l1 > r1) continue; int l2 = max(0, ALL - D1 - j), r2 = D0 - j; if(l2 > r2) continue; int vf = pre_f[r1]; if(l1) vf += p - pre_f[l1-1]; int vg = pre_g[r2]; if(l2) vg += p - pre_g[l2-1]; pls(res, (ll)vf * vg%p * F[i][j]%p); } printf("%d\n", res); } int main() { int T; read(T); while(T--) work(); return 0; }