解:观察一波部分分。
首先小数据直接暴力4n,然后考虑背包。设f[i][a][b][c]表示前i个学校中前三位导师分别有多少人,第四位导师可以直接推出来。
然后暴力枚举每一个人放在哪进行背包。
进一步发现,因为限制条件全是跟行列有关的,所以我们设f[i][a][b]表示前i个学校,第一列和第一行分别有多少人。这样也能够控制满足那4个限制。
于是我们有了一个m2n的50分DP。
然后发现k = 0的时候行列独立。于是对行列分别DP,然后乘起来,这样有70分。
1 #include <bits/stdc++.h> 2 3 const int N = 2510, MO = 998244353; 4 5 int C0, D0, C1, D1, n, c, belong[N], s[N], sum[N], K, d[N], lm[5]; 6 int f[2][2][N][N]; 7 std::vector<int> v[N]; 8 9 inline void clear() { 10 for(int i = 1; i <= n; i++) { 11 v[i].clear(); 12 sum[i] = 0; 13 d[i] = 0; 14 } 15 memset(f, 0, sizeof(f)); 16 return; 17 } 18 19 inline void solve() { 20 scanf("%d%d%d%d%d%d", &n, &c, &C0, &C1, &D0, &D1); 21 lm[1] = std::min(C0, D0); 22 lm[2] = std::min(C0, D1); 23 lm[3] = std::min(C1, D0); 24 lm[4] = std::min(C1, D1); 25 for(int i = 1; i <= n; i++) { 26 scanf("%d%d", &belong[i], &s[i]); 27 } 28 scanf("%d", &K); 29 for(int i = 1, x; i <= K; i++) { 30 scanf("%d", &x); 31 scanf("%d", &d[x]); 32 d[x]++; 33 } 34 35 for(int i = 1; i <= n; i++) { 36 v[belong[i]].push_back(i); 37 sum[belong[i]] += s[i]; 38 } 39 40 if(n > 30) { 41 42 #define h0 f[0][0][0] 43 #define h1 f[1][1][1] 44 45 h0[0] = h1[0] = 1; 46 int tot = 0; 47 for(int i = 1; i <= c; i++) { 48 int limit = v[i].size(); 49 if(!limit) continue; 50 int Sum = 0; 51 for(int j = 0; j < limit; j++) { 52 int t = v[i][j]; /// city i school t 53 Sum += s[t]; 54 for(int V = D0; V >= s[t]; V--) { 55 (h0[V] += h0[V - s[t]]) %= MO; 56 } 57 } 58 for(int V = C0; V >= Sum; V--) { 59 (h1[V] += h1[V - Sum]) %= MO; 60 } 61 tot += Sum; 62 } 63 64 int ans = 0; 65 for(int V1 = std::max(0, tot - D1); V1 <= D0; V1++) { 66 for(int V2 = std::max(0, tot - C1); V2 <= C0; V2++) { 67 (ans += 1ll * h0[V1] * h1[V2] % MO) %= MO; 68 } 69 } 70 printf("%d ", ans); 71 72 #undef h0 73 #undef h1 74 75 return; 76 } 77 78 int Sum = 0, FLAG = 0; 79 f[0][1][0][0] = f[0][0][0][0] = 1; 80 for(int i = 1; i <= c; i++) { 81 int limit = v[i].size(); 82 if(!limit) continue; 83 for(int j = 0; j < limit; j++) { 84 int t = v[i][j]; /// city i school t 85 Sum += s[t]; 86 FLAG ^= 1; 87 int (*f0)[N] = f[FLAG][0], (*f1)[N] = f[FLAG][1], (*g0)[N] = f[FLAG ^ 1][0], (*g1)[N] = f[FLAG ^ 1][1]; 88 for(int V1 = 0; V1 <= D0 && V1 <= Sum; V1++) { 89 for(int V2 = 0; V2 <= C0 && V2 <= Sum; V2++) { 90 if(d[t] != 1 && V1 >= s[t] && V2 >= s[t]) /// 1 91 f0[V1][V2] = g0[V1 - s[t]][V2 - s[t]]; 92 else 93 f0[V1][V2] = 0; 94 if(d[t] != 2 && V2 >= s[t] && Sum - V1 >= s[t]) /// 2 95 (f0[V1][V2] += g0[V1][V2 - s[t]]) %= MO; 96 if(d[t] != 3 && V1 >= s[t] && Sum - V2 >= s[t]) /// 3 97 f1[V1][V2] = g1[V1 - s[t]][V2]; 98 else 99 f1[V1][V2] = 0; 100 if(d[t] != 4 && Sum - V1 >= s[t] && Sum - V2 >= s[t]) /// 4 101 (f1[V1][V2] += g1[V1][V2]) %= MO; 102 } 103 } 104 } 105 /// 106 for(int V1 = 0; V1 <= Sum; V1++) { 107 for(int V2 = 0; V2 <= Sum; V2++) { 108 (f[FLAG][0][V1][V2] += f[FLAG][1][V1][V2]) %= MO; 109 f[FLAG][1][V1][V2] = f[FLAG][0][V1][V2]; 110 } 111 } 112 } 113 114 int ans = 0; 115 for(int V1 = std::max(0, Sum - D1); V1 <= D0; V1++) { 116 for(int V2 = std::max(0, Sum - C1); V2 <= C0; V2++) { 117 (ans += f[FLAG][0][V1][V2]) %= MO; 118 } 119 } 120 printf("%d ", ans); 121 return; 122 } 123 124 int main() { 125 126 freopen("in.in", "r", stdin); 127 freopen("right.out", "w", stdout); 128 129 int T; 130 scanf("%d", &T); 131 while(T--) { 132 solve(); 133 clear(); 134 } 135 return 0; 136 }
正解:
把上面两种部分分结合起来。发现无标号的行列,有标号这三个东西全都互相独立。
具体来说,把城市和学校都分成两类,有标记和没标记。如果一个学校有标记那么它城市也有标记。然后枚举每个无标记城市,对上下做DP。然后枚举每个无标记学校,对左右做DP。
然后对有标记的进行50分DP。这里有一个坑点......当你一个城市总人数大于C0但是受限制人数小于C0的时候,你可能会多算一种方案,即受限制的学校在上面,但是别的却在下面。
出现这个问题的原因是∑school != city,因为有些无标记学校已经拿出去算了。
所以我们要想办法把一个城市的有无标记的学校都限制在同一横条。具体来说......之前DP的时候,我们是每加一个学校就同时处理行列限制,而现在是先分成上下两部分,然后分别转移每个学校的左右。最后转移这个城市的上下,相当于为那些无标记的学校预留了位置。
记得把上下界开松一点......还有就是对城市DP的时候跳过空城市。
最后统计答案的时候,对于有标记的每一个状态,考虑把无标记的怎么塞进去。有个上下界的限制,在这个上下界之中的每个方法都是合法的,于是我们对于无标记的DP数组做前缀和,然后相乘就行了...
1 #include <bits/stdc++.h> 2 3 const int N = 5010, MO = 998244353; 4 5 int C0, D0, C1, D1, n, c, belong[N], s[N], K, d[N], h0[N], h1[N], sum[N]; 6 int f[2][2][N][N], tot; 7 bool vis[N]; 8 std::vector<int> v[N]; 9 10 inline void clear() { 11 for(int i = 1; i <= n || i <= c; i++) { 12 v[i].clear(); 13 sum[i] = d[i] = vis[i] = 0; 14 } 15 tot = 0; 16 memset(f, 0, sizeof(f)); 17 memset(h0, 0, sizeof(h0)); 18 memset(h1, 0, sizeof(h1)); 19 return; 20 } 21 22 inline int cal(int V1, int V2) { 23 int l1 = std::max(0, tot - D1 - V1), r1 = D0 - V1; 24 int l2 = std::max(0, tot - C1 - V2), r2 = C0 - V2; 25 if(l1 > r1 || l2 > r2) return 0; 26 int temp1 = (h0[r1] - (l1 ? h0[l1 - 1] : 0) + MO) % MO, temp2 = (h1[r2] - (l2 ? h1[l2 - 1] : 0) + MO) % MO; 27 return 1ll * temp1 * temp2 % MO; 28 } 29 30 inline void solve() { 31 scanf("%d%d%d%d%d%d", &n, &c, &C0, &C1, &D0, &D1); 32 for(int i = 1; i <= n; i++) { 33 scanf("%d%d", &belong[i], &s[i]); 34 v[belong[i]].push_back(i); 35 sum[belong[i]] += s[i]; 36 tot += s[i]; 37 } 38 scanf("%d", &K); 39 for(int i = 1, x; i <= K; i++) { 40 scanf("%d", &x); 41 scanf("%d", &d[x]); 42 d[x]++; 43 vis[belong[x]] = 1; 44 } 45 46 int LM = std::max(tot, std::max(C0 + C1, D0 + D1)); 47 /// first no limit DP 48 h0[0] = h1[0] = 1; 49 int Sum = 0; 50 for(int i = 1; i <= n; i++) { 51 if(d[i]) continue; 52 Sum += s[i]; 53 for(int V = Sum; V >= s[i]; V--) { 54 (h0[V] += h0[V - s[i]]) %= MO; 55 } 56 } 57 for(int i = 1; i <= LM; i++) { 58 (h0[i] += h0[i - 1]) %= MO; 59 } 60 61 Sum = 0; 62 for(int i = 1; i <= c; i++) { 63 if(vis[i] || !sum[i]) continue; 64 Sum += sum[i]; 65 for(int V = Sum; V >= sum[i]; V--) { 66 (h1[V] += h1[V - sum[i]]) %= MO; 67 } 68 } 69 for(int i = 1; i <= LM; i++) { 70 (h1[i] += h1[i - 1]) %= MO; 71 } 72 73 /// second limit DP 74 Sum = 0; 75 int FLAG = 0, Sum2 = 0; 76 f[0][1][0][0] = f[0][0][0][0] = 1; 77 for(int i = 1; i <= c; i++) { 78 int limit = v[i].size(); 79 if(!limit || !vis[i]) continue; 80 Sum2 += sum[i]; 81 for(int j = 0; j < limit; j++) { 82 int t = v[i][j]; /// city i school t 83 if(!d[t]) continue; 84 Sum += s[t]; 85 FLAG ^= 1; 86 int (*f0)[N] = f[FLAG][0], (*f1)[N] = f[FLAG][1], (*g0)[N] = f[FLAG ^ 1][0], (*g1)[N] = f[FLAG ^ 1][1]; 87 for(int V1 = 0; V1 <= D0 && V1 <= Sum; V1++) { 88 for(int V2 = 0; V2 <= C0 && V2 <= Sum2; V2++) { 89 if(d[t] != 1 && V1 >= s[t]) /// 1 90 f0[V1][V2] = g0[V1 - s[t]][V2]; 91 else 92 f0[V1][V2] = 0; 93 if(d[t] != 2 && Sum - V1 >= s[t]) /// 2 94 (f0[V1][V2] += g0[V1][V2]) %= MO; 95 96 if(d[t] != 3 && V1 >= s[t]) /// 3 97 f1[V1][V2] = g1[V1 - s[t]][V2]; 98 else 99 f1[V1][V2] = 0; 100 if(d[t] != 4 && Sum - V1 >= s[t]) /// 4 101 (f1[V1][V2] += g1[V1][V2]) %= MO; 102 } 103 } 104 } 105 /// 106 107 FLAG ^= 1; 108 for(int V1 = 0; V1 <= Sum && V1 <= D0; V1++) { 109 for(int V2 = 0; V2 <= Sum2 && V2 <= C0; V2++) { 110 /// up 111 if(V2 >= sum[i]) 112 f[FLAG][0][V1][V2] = f[FLAG ^ 1][0][V1][V2 - sum[i]]; 113 else 114 f[FLAG][0][V1][V2] = 0; 115 /// down 116 if(Sum2 - V2 >= sum[i]) 117 (f[FLAG][0][V1][V2] += f[FLAG ^ 1][1][V1][V2]) %= MO; 118 f[FLAG][1][V1][V2] = f[FLAG][0][V1][V2]; 119 } 120 } 121 } 122 123 int ans = 0; 124 for(int V1 = 0; V1 <= D0; V1++) { 125 for(int V2 = 0; V2 <= C0; V2++) { 126 (ans += 1ll * f[FLAG][0][V1][V2] * cal(V1, V2) % MO) %= MO; 127 } 128 } 129 printf("%d ", ans); 130 return; 131 } 132 133 int main() { 134 135 int T; 136 scanf("%d", &T); 137 while(T--) { 138 solve(); 139 if(T) { 140 clear(); 141 } 142 } 143 return 0; 144 }
这TM考场上谁能写出来啊....十个我也搞不倒......