《如 何 速 通 一 套 题》6.0

hhc0001deljbk / 2024-09-26 / 原文

邮寄

开场秒掉 A。

然后陷入了无尽的 BCD 循环中......

C 推了我好久,然后没推出来......

A 集合

考虑每一个子集和的贡献。设 \(dp_i\) 表示子集和为 \(i\) 的方案数,转移易推。

答案是

\[\prod \limits_{i = 1}^{\frac{n(n + 1)}{2}} i^{dp_i} \]

但是 \(n\)\(200\) 会原地起飞,没事,我们可以取模......

注意!!

对于指数上的数,千万不要 \(\bmod 998244353\),要 \(\bmod 998244352\)

根据费马小定理,\(a^{\color{red}{p - 1}} \equiv 1 \pmod p\)

然后就可以了......

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int kMod = 998244353;

int n, dp[40040], lm, ans = 1;

int qpow(int x, int y) {
  int rs = 1;
  for(; y; y >>= 1) {
    if(y & 1) {
      rs = rs * x % kMod;
    }
    x = x * x % kMod;
  }
  return rs;
}

signed main() {
  freopen("set.in", "r", stdin);
  freopen("set.out", "w", stdout);
  cin >> n;
  lm = n * (n + 1) / 2;
  dp[0] = 1;
  for(int i = 1; i <= n; i++) {
    for(int j = lm - i; j >= 0; j--) {
      dp[j + i] = (dp[j + i] + dp[j]) % (kMod - 1); // -1 is key
    }
  }
  for(int i = 1; i <= lm; i++) {
    ans = ans * qpow(i, dp[i]) % kMod;
  }
  cout << ans << '\n';
  return 0;
}

B 出租

考虑一个区间 \([l, r]\) 里面的人。

可以写出如下方程:

\[\sum \limits_{i = l}^r val_i \leq k(r - l + 1 + d) \]

稍微变形一下:

\[\sum \limits_{i = l}^r val_i \leq k(r - l + 1 + d) \]

\[\to \sum \limits_{i = l}^r val_i \leq k(r - l + 1) + kd \]

\[\to \sum \limits_{i = l}^r (val_i - k) \leq kd \]

维护左边的最大子段和,然后就可以直接判断了。

如何动态求解最大子段和(Spoiler Alert!)

动态维护,果断祭出线段树。

维护如下四个信息:

\(pm, sm, as, fs\) 代表(非空)前缀最大值,后缀最大值,最大子段和,区间和,

合并如下:

\(pm = \max(pm_{ls}, fs_{ls} + pm_{rs})\)

\(sm = \max(sm_{rs}, fs_{rs} + sm_{ls})\)

\(as = \max(as_{ls}, as_{rs}, sm_{ls} + pm_{rs})\)

\(fs = fs_{ls} + fs_{rs}\)

由于只有单点修改,所以不需要多少维护。

#include <bits/stdc++.h>
#define int long long
using namespace std;

struct dat {
  int pm, sm, as, fs;
  
  dat(int s = -1e10, int p = -1e10, int d = -1e10, int f = -1e10) : pm(), sm(), as(), fs() {
    pm = s, sm = p, as = d, fs = f;
  }
  
  dat operator ^(const dat &b) const {
    return dat(max(pm, fs + b.pm), max(sm + b.fs, b.sm), max({as, b.as, sm + b.pm}), fs + b.fs);
  }
}d[2000020];

int n, m, k, l, arr[500050], x, y;

void build(int id = 1, int l = 1, int r = n) {
  if(l == r) {
    d[id] = {arr[l], arr[l], arr[l], arr[l]};
    return ;
  }
  int mid = (l + r) >> 1;
  build(id * 2, l, mid);
  build(id * 2 + 1, mid + 1, r);
  d[id] = d[id * 2] ^ d[id * 2 + 1];
}

void modify(int x, int v, int id = 1, int l = 1, int r = n) {
  if(l == r) {
    arr[l] = v;
    d[id] = {arr[l], arr[l], arr[l], arr[l]};
    return ;
  }
  int mid = (l + r) >> 1;
  if(x <= mid) {
    modify(x, v, id * 2, l, mid);
  }else {
    modify(x, v, id * 2 + 1, mid + 1, r);
  }
  d[id] = d[id * 2] ^ d[id * 2 + 1];
}

dat query(int ql, int qr, int id = 1, int l = 1, int r = n) {
  if(r < ql || qr < l) {
    return dat();
  }
  if(ql <= l && r <= qr) {
    return d[id];
  }
  int mid = (l + r) >> 1;
  dat a = query(ql, qr, id * 2, l, mid), b = query(ql, qr, id * 2 + 1, mid + 1, r);
  return a ^ b;
}

signed main() {
  freopen("hire.in", "r", stdin);
  freopen("hire.out", "w", stdout);
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n >> m >> k >> l;
  for(int i = 1; i <= n; i++) {
    arr[i] -= k;
  }
  build();
  for(; m--; ) {
    cin >> x >> y;
    modify(x, arr[x] + y);
    dat tmp = query(1, n);
    cout << (tmp.as > k * l? "NO" : "YES") << '\n';
  }
  return 0;
}

C 连通块

观察到我们只需要处理那些“不共戴天”的节点就可以了。

然后就自然可以 dp 了。

#include <bits/stdc++.h>
#define int ll
using namespace std;
using ll = long long;

struct node {
  int u, v;
}cnd[22];

int n, m, val[100010], l, bg[44][44], tot, id[100010], dp[100010][44], ans;
vector<int> ch[100010];

void DFS(int x = 1) {
  for(int i = 0; i <= tot; i++) {
    dp[x][i] = -1e16;
  }
  dp[x][id[x]] = val[x];
  for(auto i : ch[x]) {
    DFS(i);
    int mx = -1e16;
    for(int j = 0; j <= tot; j++) {
      if(!bg[j][id[i]]) {
        mx = max(mx, dp[x][j]);
      }
    }
    for(int j = 0; j <= tot; j++) {
      dp[x][j] = max(dp[x][j], dp[i][j] + mx);
    }
  }
  for(int j = 0; j <= tot; j++) {
    ans = max(ans, dp[x][j]);
  }
}

signed main() {
  freopen("block.in", "r", stdin);
  freopen("block.out", "w", stdout);
  cin >> n >> m;
  for(int i = 1; i <= n; i++) {
    cin >> val[i];
  }
  for(int i = 1; i <= n; i++) {
    cin >> l;
    ch[i].resize(l);
    for(auto &j : ch[i]) {
      cin >> j;
    }
  }
  ans = -1e16;
  /*
  for(int i = 1; i <= n; i++) {
    ans = max(ans, val[i]);
  }
  if(ans <= 0) {
    cout << ans << '\n';
    return 0;
  }
  //*/
  for(int i = 1; i <= m; i++) {
    cin >> cnd[i].u >> cnd[i].v;
    if(!id[cnd[i].u]) {
      id[cnd[i].u] = ++tot;
    }
    if(!id[cnd[i].v]) {
      id[cnd[i].v] = ++tot;
    }
    bg[id[cnd[i].u]][id[cnd[i].v]] = bg[id[cnd[i].v]][id[cnd[i].u]] = 1;
  }
  DFS();
  cout << ans << '\n';
  return 0;
}

D 跳棋

我们发现把所有的单个 \(1\) 拿出来后(奇数段中的一个 \(1\) 也算单个一,但是一个段中只有一个),剩下的 \(11\)\(0\) 可以随便安排。

所以设 \(dp_{i, j, k, 0/1}\) 表示考虑了前 \(i\) 个,有 \(j\)\(11\),有 \(k\)\(0\),末尾是否可以加一个 \(1\) 变成 \(11\)

\(0\) 时:\(dp_{i, j, k, 0/1} \to dp_{i + 1, j, k + 1, 0}\)

\(1\) 时:\(dp_{i, j, k, 0} \to dp_{i + 1, j, k, 1}, dp_{i, j, k, 1} \to dp_{i + 1, j + 1, k, 0}\)

然后答案是

\[\sum \limits_{i = 1}^n \sum \limits_{j = 1}^n (dp_{n, i, j, 0} + dp_{n, i, j, 1})\binom{i + j}{j} \]

#include <bits/stdc++.h>
#define int ll
using namespace std;
using ll = long long;

const int kMod = int(1e9) + 7;

int n, fac[1010], inv[1010], ans, dp[2][550][550][2];
string s;

void init() {
  fac[0] = fac[1] = 1, inv[1] = 1;
  inv[0] = 1;
  for(int i = 2; i <= 1000; i++) {
    fac[i] = fac[i - 1] * i % kMod;
    inv[i] = inv[kMod % i] * (kMod - kMod / i) % kMod;
  }
  for(int i = 2; i <= 1000; i++) {
    inv[i] = inv[i] * inv[i - 1] % kMod;
  }
}

int c(int x, int y) {
  if(x < 0 || y < 0 || x < y) {
    return 0;
  }
  return fac[x] * inv[x - y] % kMod * inv[y] % kMod;
}

signed main() {
  freopen("checkers.in", "r", stdin);
  freopen("checkers.out", "w", stdout);
  cin >> n >> s;
  n = s.size();
  dp[0][0][0][0] = 1;
  for(int i = 0; i < n; i++) {
    for(int j = 0; j <= n; j++) {
      for(int k = 0; k <= n; k++) {
        if(s[i] != '1') {
          dp[1][j][k + 1][0] = (dp[1][j][k + 1][0] + dp[0][j][k][0] + dp[0][j][k][1]) % kMod;
        }
        if(s[i] != '0') {
          dp[1][j][k][1] = (dp[1][j][k][1] + dp[0][j][k][0]) % kMod;
          dp[1][j + 1][k][0] = (dp[1][j + 1][k][0] + dp[0][j][k][1]) % kMod;
        }
      }
    }
    for(int j = 0; j <= n; j++) {
      for(int k = 0; k <= n; k++) {
        dp[0][j][k][0] = dp[1][j][k][0], dp[0][j][k][1] = dp[1][j][k][1];
        dp[1][j][k][0] = dp[1][j][k][1] = 0;
      }
    }
  }
  init();
  for(int i = 0; i <= n; i++) {
    for(int j = 0; j <= n; j++) {
      ans = (ans + (dp[0][i][j][0] + dp[0][i][j][1]) % kMod * c(i + j, j)) % kMod;
    }
  }
  cout << ans << '\n';
  return 0;
}