Skip to content

ABC 265

比赛链接

A - Apple

如果 \(3 * x \le y\),则答案为 \(x * n\),否则为 \((n / 3) * y + (n \% 3) * x\)

B - Explore

从左往右遍历即可,使用 t 表示剩余时间,遇到一个 bouns room 则增加 t。直到 t 无法到达下一个房间。

C - Belt Conveyor

使用一个二维表记录每个位置是否走过,当走出地图外或者走到一个曾经走过的地方时就结束循环。

D - Iroha and Haiku

分三次DP,第一次只需要找合法的 \(y\),然后第二次找合法的 \(z\),以为对于每个合法的 \(z\),其对应的 \(y\) 是固定的(所有元素都为正),所以直接考虑是否可以从这个 \(y\) 转移即可。第三次 DP 是类似的。

复杂度为 \(O(n\log n)\),通过双指针可以优化到 \(O(n)\)

void solve() {
  int n; ll p, q, r;
  cin >> n >> p >> q >> r;
  vector<ll> a(n + 1), s(n + 1);
  for(int i = 1; i <= n; i++) {
    cin >> a[i];
    s[i] = s[i - 1] + a[i];
  }
  vector<int> f(n + 1), d(n + 1), w(n + 1);
  map<ll, int> mp;
  mp[0] = 0;
  for(int i = 1; i <= n; i++) {
    if(mp.count(s[i] - p)) {
      f[i] = 1;
    }
    mp[s[i]] = i;
  }

  mp.clear(); mp[0] = 0;
  for(int i = 1; i <= n; i++) {
    if(mp.count(s[i] - q)) {
      int x = mp[s[i] - q];
      if(x > 0 && f[x]) {
        d[i] = 1;
      } 
    }
    mp[s[i]] = i;
  }
  mp.clear(); mp[0] = 0;
  for(int i = 1; i <= n; i++) {
    if(mp.count(s[i] - r)) {
      int x = mp[s[i] - r];
      if(x > 0 && d[x]) {
        cout << "Yes\n";
        return;
      }
    }
    mp[s[i]] = i;
  }

  cout << "No\n";
}

E - Warp

\(d[i][j][k]\) 表示进行了 \(i\) 次 1 操作,\(j\) 次 2 操作,\(k\) 次 3 操作后可行的方案数。而此时的坐标可以根据 \((i, j, k)\) 计算得到,通过查表可以知道该位置是否合法,如果不合法则不转移,否则:

  • d[i + 1][j][k] += d[i][j][k]
  • d[i][j + 1][k] += d[i][j][k]
  • d[i][j][k + 1] += d[i][j][k]

时间复杂度为 \(O(n^3\log m)\)

constexpr int P = 998244353;
// assume -P <= x < 2P
int norm(int x) { if (x < 0) x += P; else if (x >= P) x -= P; return x; }
// 快速幂
template <class T> T power(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }
struct Int {
    int x;
    Int(int x = 0) : x(norm(x)) {}
    int val() const { return x; }
    Int operator-() const { return Int(norm(P - x)); }
    Int inv() const { assert(x != 0); return power(*this, P - 2); }
    Int &operator*=(const Int &rhs) { x = ll(x) * rhs.x % P; return *this; }
    Int &operator+=(const Int &rhs) { x = norm(x + rhs.x); return *this; }
    Int &operator-=(const Int &rhs) { x = norm(x - rhs.x); return *this; }
    Int &operator/=(const Int &rhs) { return *this *= rhs.inv(); }
    friend Int operator*(const Int &lhs, const Int &rhs) { Int res = lhs; res *= rhs; return res; }
    friend Int operator+(const Int &lhs, const Int &rhs) { Int res = lhs; res += rhs; return res; }
    friend Int operator-(const Int &lhs, const Int &rhs) { Int res = lhs; res -= rhs; return res; }
    friend Int operator/(const Int &lhs, const Int &rhs) { Int res = lhs; res /= rhs; return res; }
    friend ostream& operator << (ostream& out, const Int &x) { out << x.val(); return out; }
};
Int d[301][301][301];
void solve() {
  int n, m;
  ll A, B, C, D, E, F;
  cin >> n >> m;
  cin >> A >> B >> C >> D >> E >> F;
  map<pair<ll, ll>, int> mp;
  for(int i = 1; i <= m; i++) {
    int x, y; cin >> x >> y;
    mp[make_pair(x, y)] = 1;
  }

  d[0][0][0] = 1;
  Int res = 0;
  for(int i = 0; i <= n; i++) {
    for(int j = 0; j + i <= n; j++) {
      for(int k = 0; i + j + k <= n; k++) {
        ll x = i * A + j * C + k * E;
        ll y = i * B + j * D + k * F;
        if(mp.count({x, y})) continue;
        if(i + j + k == n) {
          res += d[i][j][k];
          break;
        }
        d[i + 1][j][k] += d[i][j][k];
        d[i][j + 1][k] += d[i][j][k];
        d[i][j][k + 1] += d[i][j][k];
      }
    }
  }
  cout << res << "\n";
}

F - Manhattan Cafe

\(f[x][y]\)\(d(p,r) = x, d(q, r) = y\) 时的方案数。我们可以构造一个类似背包DP的转移,复杂度为 \(O(nd^3)\)

优化思路是,每个阶段转移时,物品体积是有规律的。设当前 \(s = abs(p[i] - q[i])\),那么转移有: 1. \((s, 0),(s-1, 1),\cdots, (0, s)\) 2. \((s+1, 1), (s+2, 2), \cdots\) 3. \((1, s+1), (2, s+2), \cdots\)

可以发现,这三组中的每一组,对应于二维矩阵中的一条斜线。所以可以使用前缀和优化转移。

时间复杂度为 \(O(nd^2)\)

void solve() {
  int n, d;
  cin >> n >> d;
  vector<int> p(n), q(n);
  for(int i = 0; i < n; i++) cin >> p[i];
  for(int i = 0; i < n; i++) cin >> q[i];

  vector<vector<Int>> f(d + 1, vector<Int>(d + 1, 0));
  f[0][0] = 1;

  for(int i = 0; i < n; i++) {
    int s = abs(p[i] - q[i]);
    vector<vector<Int>> g(d + 1, vector<Int>(d + 1, 0));
    auto sum = f;
    for(int x = 0; x <= d; x ++) {
      for(int y = 0; y <= d; y++) {
        if(x - 1 >= 0 && y + 1 <= d) {
          sum[x][y] += sum[x - 1][y + 1];
        }

        if(x + y < s) continue;
        int xx = x, yy = y - s;
        if(yy < 0) {
          xx += yy;
          yy = 0;
        }
        if(xx >= 0 && xx <= d && yy >= 0 && yy <= d) g[x][y] += sum[xx][yy];

        xx = x - s - 1, yy = y + 1;
        if(xx >= 0 && xx <= d && yy >= 0 && yy <= d) g[x][y] -= sum[xx][yy];
      }
    }

    sum = f;
    for(int x = 0; x <= d; x ++) {
      for(int y = 0; y <= d; y ++) {
        if(x > 0 && y > 0) sum[x][y] += sum[x - 1][y - 1];
        if(x - (s + 1) >= 0 && y - 1 >= 0) g[x][y] += sum[x - (s + 1)][y - 1];
        if(x - 1 >= 0 && y - (s + 1) >= 0) g[x][y] += sum[x - 1][y - (s + 1)];
      }
    }

    f = g;
  }

  Int res = 0;
  for(auto &v : f) for(auto &x : v) res += x;
  cout << res << "\n";
}

G - 012 Inversion

线段树单个节点维护一个二维矩阵 \(pairs\)\(pairs[i][j]\) 对应的值表示该节点表示的区间中形成前面是 \(i\),后面是 \(j\) 的对数。另外,还需要一个数组 \(cnt[i]\) 表示区间中 \(i\) 的个数。

struct Info {
  vector<vector<ll>> pairs;
  vector<ll> cnt;
  Info(): pairs(3, vector<ll>(3, 0)), cnt(3, 0) {}
  ll sum() {
    return pairs[1][0] + pairs[2][1] + pairs[2][0];
  }
};

// 两个节点合并时对应的操作
struct Merge {
  Info operator () (const Info &l, const Info &r) const {
    Info t;
    for(int i = 0; i < 3; i++) {
      for(int j = 0; j < 3; j++) {
        t.pairs[i][j] = l.pairs[i][j] + r.pairs[i][j] + l.cnt[i] * r.cnt[j];
      }
    }
    for(int i = 0; i < 3; i++) t.cnt[i] = l.cnt[i] + r.cnt[i];
    return t;
  }
};

LazyTag 维护一个映射关系,用一个长度为 3 的数组表示。

struct LazyTag {
  vector<int> trans;
  LazyTag(): LazyTag(0, 1, 2) {}
  LazyTag(int S, int T, int U): trans(3, 0) {
    trans[0] = S; trans[1] = T; trans[2] = U;
  }
};

// tag 作用于节点
struct Mapping {
  void operator () (const LazyTag& tag, Info& info) const {
    if(tag.trans != vector<int>{0, 1, 2}) {

      vector<vector<ll>> pairs(3, vector<ll>(3, 0));
      for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
          int u = tag.trans[i], v = tag.trans[j];
          pairs[u][v] += info.pairs[i][j]; // 注意这里是 += 而非 =
        }
      }
      info.pairs = pairs;

      vector<ll> cnt(3);
      for(int i = 0; i < 3; i++) cnt[tag.trans[i]] += info.cnt[i]; // 同理是 += 而非 =
      info.cnt = cnt;
    }
  }
};


// 再加一层映射
struct Composition {
  void operator ()(const LazyTag& x, LazyTag& y) const {
    if(x.trans != vector<int>{0, 1, 2}) {
      // dbg(x.trans);
      for(int i = 0; i < 3; i++) {
        y.trans[i] = x.trans[y.trans[i]];
      }
    }
  }
};

其他代码:

template<class Info, class LazyTag, 
  class Merge, class Mapping, class Composition>
struct LazySegTree{
  int n;
  Merge merge;
  Mapping mapping;
  Composition composition;
  vector<Info> info;
  vector<LazyTag> lazyTag;
  LazySegTree(int n): n(n), merge(Merge()), mapping(Mapping()), 
    composition(Composition()), info(4 * n), lazyTag(4 * n) {}
  LazySegTree(int n, vector<Info> &init): LazySegTree(n) {
    function<void(int,int,int)> build = [&](int p, int l, int r) {
      if(l == r) {
        info[p] = init[l];
        return;
      }
      int m = (l + r) / 2;
      build(2 * p, l, m);
      build(2 * p + 1, m + 1, r);
      pull(p);
    };
    build(1, 0, n - 1);
  }

  void pull(int p) {
    info[p] = move(merge(info[2 * p], info[2 * p + 1]));
  }

  void push(int p, const LazyTag& tag) {
    mapping(tag, info[p]);
    composition(tag, lazyTag[p]);
  }

  void pushdown(int p) {
    push(2 * p, lazyTag[p]);
    push(2 * p + 1, lazyTag[p]);
    lazyTag[p] = LazyTag();
  }
  // 这里是 LazyTag
  void modify(int p, int l, int r, int L, int R, const LazyTag &v) {
    if(l >= L && r <= R) {
      push(p, v);
      return;
    }
    pushdown(p);
    int m = (l + r) / 2;
    if(L <= m) modify(p * 2, l, m, L, R, v);
    if(R > m) modify(2 * p + 1, m + 1, r, L, R, v);
    pull(p);
  }

  void modify(int l, int r, const LazyTag &v) {
    modify(1, 0, n - 1, l, r, v);
  }

  Info rangeQuery(int p, int l, int r, int x, int y) {
    if(l > y || r < x) {
      return Info();
    }
    if(l >= x && r <= y) return info[p];
    int m = (l + r) / 2;
    pushdown(p);
    return merge(rangeQuery(p * 2, l, m, x, y), rangeQuery(p * 2 + 1, m + 1, r, x, y));
  }

  Info rangeQuery(int l, int r) {
    return rangeQuery(1, 0, n - 1, l, r);
  }
};

void solve() {
  int n, q;
  cin >> n >> q;
  vector<Info> info(n);
  for(int i = 0; i < n; i++) {
    int x; cin >> x;
    info[i].cnt[x] ++;
  }
  LazySegTree<Info, LazyTag, Merge, Mapping, Composition> seg(n, info);
  while(q--) {
    int op, l, r, s, t, u;
    cin >> op >> l >> r;
    if(op == 1) {
      cout << seg.rangeQuery(l - 1, r - 1).sum() << "\n";
    } else {
      cin >> s >> t >> u;
      seg.modify(l - 1, r - 1, LazyTag(s, t, u));
    }
  }
}

本文访问