[十二省联考2019]皮配 - 背包、dp、计数

Description

题面

简化版题面:

\(c\) 个豆荚,共 \(n\) 颗豆子,每颗豆子都有自己的重量,现在需要将给豆子设定为(黄色/绿色,圆粒/皱粒),要求满足以下条件:

  1. 给定这四种性状的阀值 \(C0,C1,D0,D1\),要求为这种性状的豆子重量和不能超过该阀值。

  2. 与此同时,这 \(n\) 颗豆子中存在 \(k\) 颗顽皮豆,顽皮豆都有自己的想法,比如拒绝成为(黄圆/黄皱/绿圆/绿皱)。

  3. 同一个豆荚里的豆子必须同时为黄色同时为绿色

求有多少种给豆子设定的方案,答案对 \(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\) 个人的方案数,那么分别计算出来后,进行合并,即总方案数为

\[\sum \limits_{i=S-C_1}^{C_0} \sum \limits_{i=D_1}^{D_0} (f_{n,0,i} + f_{n,1,i}) \times g_{n,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:

  1. 滚动数组每轮记得清零!
  2. 不要用 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;
}
posted @ 2020-06-17 08:04  newbielyx  阅读(277)  评论(0编辑  收藏  举报