洛谷P5289 皮配

解:观察一波部分分。

首先小数据直接暴力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 }
70分代码

正解:

把上面两种部分分结合起来。发现无标号的行列,有标号这三个东西全都互相独立。

具体来说,把城市和学校都分成两类,有标记和没标记。如果一个学校有标记那么它城市也有标记。然后枚举每个无标记城市,对上下做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 }
AC代码

这TM考场上谁能写出来啊....十个我也搞不倒......

原文地址:https://www.cnblogs.com/huyufeifei/p/10726002.html