微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

NOMURA Programming Contest 2022AtCoder Beginner Contest 253

A就是模拟一下。

B就是曼哈顿距离。

C就是std::map搞一搞。

D就是容斥一下。

E - Distance Sequence

题意

问满足下列条件的数组\(a\)有多少种:

  • 长度为\(n\)
  • 值域为\([1, m]\)
  • 相邻元素差值的绝对值大于\(k\)

其中\(n \le 1000, m \le 5000, k \le m - 1\)。

思路

\(O(nm)\)的DP就能搞定。

令\(dp_{i, j}\)表示长度为\(i\),满足题目给定条件,且\(a_i = j\)的方案数。

那么有\(dp_{i, j} = \sum_{|x - j| \ge k} dp_{i - 1, x}\),朴素的做法是\(O(nm^2)\)的。

注意到其实满足条件的\(x\)其实是\([1, i - k] \cup [i + k, m]\),加个前缀和优化就能做到\(O(nm)\)了。

AC代码
// Problem: E - distance Sequence
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

void solve_case(int Case) {
  int mod = 998244353;

  int n, m, k;
  std::cin >> n >> m >> k;

  if (k == 0) {
    int ans = 1;
    for (int i = 1; i <= n; ++i)
      ans = i64(1) * ans * m % mod;
    std::cout << ans << "\n";
    return;
  }

  std::vector<int> dp(m + 1);
  for (int i = 1; i <= m; ++i)
    dp[i] = 1;

  for (int _ = 2; _ <= n; ++_) {
    auto temp = dp;
    for (int j = 1; j <= m; ++j)
      temp[j] = (temp[j] + temp[j - 1]) % mod;

    for (int j = 1; j <= m; ++j) {
      dp[j] = 0;
      if (j - k >= 1) {
        dp[j] = (dp[j] + ((temp[j - k] - temp[0]) % mod + mod) % mod) % mod;
      }
      if (j + k <= m) {
        dp[j] = (dp[j] + ((temp[m] - temp[j + k - 1]) % mod + mod) % mod) % mod;
      }
    }
  }

  int ans = 0;
  for (int i = 1; i <= m; ++i)
    ans = (ans + dp[i]) % mod;

  std::cout << ans << "\n";
}

F - Operations on a Matrix

题意

给定一个\(n\)行\(m\)列的矩阵,初始是矩阵中元素全为0,要求支持\(q\)个操作,操作共有一下3种:

  1. 1 l r x: 给第\(l\)列到第\(r\)列的元素都加上\(x\)。
  2. 2 i x: 将第\(i\)行的元素都置为\(x\)。
  3. 3 i j输出当前第\(i\)行第\(j\)列元素的值。

其中,\(n, m, q \le 2 \times 10^5\)。

思路1

对于第1个操作,用可持久化线段树维护。

对于第2个操作,操作时记录下当时可持久化线段树的版本号以及\(x\)。

对于第3个操作,假设第\(i\)行维护的版本号为\(old\),操作数为\(x\)。当前的版本号为\(Now\),则答案为\(x + sum(Now, j, j) - sum(old, j, j)\)。其中\(sum(v, l, r)\)表示可持久化线段树第\(i\)个版本中区间\([l, r]\)元素之和。

然后就是可持久化线段树区间加操作的维护了,指路HDU 4348。

思路2

可以借助离散化进一步简化问题。

对于每个操作3,它其实只和上一次修改第\(i\)行的操作2有关,可以把操作3挂到操作2上。

假设某个操作2\((i, x)\)之前,操作1在第\(j\)行上共加了\(A\)。对于某个操作3\((i, j)\),假设上一句提到的\((i, x)\)为该操作3前最后一个修改第\(i\)行,且此时操作1在第\(j\)行上共加了\(B\),那么该操作3的答案为\(x + B - A\)。

\(B\)的话临时查寻即可,\(x\)和\(B\)的话就是把操作3挂到操作2上,然后在操作2操作的时候就加上

现在就不需要可持久化了,普通线段树或者树状数组都可以搞。

思路1 AC代码
// Problem: F - Operations on a Matrix
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

// persistant segment tree with lazy tag.
const int maxn = 2e5 + 5;
const int SIZE = maxn << 5;
#define ll long long
#define lson l, m
#define rson m + 1, r

int rt[maxn], ls[SIZE], rs[SIZE], sz;
ll sum[SIZE], lazy[SIZE];

void pushup(int rt, int m) {
  sum[rt] = sum[ls[rt]] + sum[rs[rt]] + (ll)lazy[rt] * m;
}

void build(int& rt, int l, int r) {
  rt = ++sz;
  lazy[rt] = 0;
  if (l == r) {
    sum[rt] = 0;
    return;
  }

  int m = (l + r) >> 1;
  build(ls[rt], lson);
  build(rs[rt], rson);
  pushup(rt, r - l + 1);
}

void update(int pre, int& rt, int L, int R, int l, int r, ll c) {
  rt = ++sz;
  lazy[rt] = lazy[pre];
  sum[rt] = sum[pre];
  ls[rt] = ls[pre];
  rs[rt] = rs[pre];
  if (L <= l && r <= R) {
    lazy[rt] += c;
    sum[rt] += (ll)c * (r - l + 1);
    return;
  }

  int m = (l + r) >> 1;
  if (L <= m)
    update(ls[pre], ls[rt], L, R, lson, c);
  if (R > m)
    update(rs[pre], rs[rt], L, R, rson, c);
  pushup(rt, r - l + 1);
}

ll query(int rt, int L, int R, int l, int r) {
  if (L <= l && r <= R) {
    return sum[rt];
  }

  ll ret = (ll)lazy[rt] * (R - L + 1);
  int m = (l + r) >> 1;
  if (R <= m)
    ret += query(ls[rt], L, R, lson);
  else if (L > m)
    ret += query(rs[rt], L, R, rson);
  else
    ret += query(ls[rt], L, m, lson) + query(rs[rt], m + 1, R, rson);
  return ret;
}

void solve_case(int Case) {
  int n, m, q;
  std::cin >> n >> m >> q;

  int version = 0;
  sz = 0;
  build(rt[version], 1, m);

  std::vector<std::pair<int, int>> row(n + 1, std::pair(0, 0));

  for (int i = 1; i <= q; ++i) {
    int op;
    std::cin >> op;
    if (op == 1) {
      int l, r, x;
      std::cin >> l >> r >> x;
      ++version;
      update(rt[version - 1], rt[version], l, r, 1, m, x);
    } else if (op == 2) {
      int p, x;
      std::cin >> p >> x;
      row[p] = std::pair<int, int>(x, version);
    } else if (op == 3) {
      int x, y;
      std::cin >> x >> y;
      int old_version = row[x].second;
      ll ans = row[x].first + query(rt[version], y, y, 1, m) - query(rt[old_version], y, y, 1, m);
      std::cout << ans << "\n";
    }
  }
}

思路2 AC代码
// Problem: F - Operations on a Matrix
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

template <typename T>
struct FenwickTree {
  int n;
  std::vector<T> c;
  FenwickTree(int _n) : n(_n), c(n + 1) {}
  inline int lb(int x) { return x & -x; }
  void add(int x, T d) {
    for (; x <= n; x += lb(x))
      c[x] += d;
  }
  T getsum(int x) {
    T r = 0;
    for (; x; x -= lb(x))
      r += c[x];
    return r;
  }
  T getsum(int l, int r) { return getsum(r) - getsum(l - 1); }
};

void solve_case(int Case) {
  int n, m, q;
  std::cin >> n >> m >> q;

  FenwickTree<i64> t(m);
  std::vector<std::array<int, 4>> a(q + 1);
  std::vector<std::vector<int>> QueryAt(q + 1);
  std::vector<int> last(n + 1);

  std::vector<i64> ans;

  for (int i = 1; i <= q; ++i) {
    int op;
    std::cin >> op;

    if (op == 1) {
      int l, r, d;
      std::cin >> l >> r >> d;
      a[i] = {op, l, r, d};
    } else if (op == 2) {
      int x, d;
      std::cin >> x >> d;
      a[i] = {op, x, d, i};

      last[x] = i;
    } else if (op == 3) {
      int x, y;
      std::cin >> x >> y;

      int ans_id = ans.size();
      ans.push_back(0);

      a[i] = {op, x, y, ans_id};

      int last_x = last[x];
      QueryAt[last_x].push_back(i);
    }
  }

  for (int i = 1; i <= q; ++i) {
    int op = a[i][0];
    if (op == 1) {
      auto [_, l, r, d] = a[i];
      t.add(l, d);
      t.add(r + 1, -d);
    } else if (op == 2) {
      auto [_1, _2, d, _3] = a[i];

      for (int query_id : QueryAt[i]) {
        auto [_1, _2, y, ans_id] = a[query_id];
        ans[ans_id] += d;
        ans[ans_id] -= t.getsum(y);
      }
    } else if (op == 3) {
      auto [_1, _2, y, ans_id] = a[i];
      ans[ans_id] += t.getsum(y);
    }
  }

  for (int i = 0; i < ans.size(); ++i)
    std::cout << ans[i] << "\n";
}


G - Swap Many Times

题意

将所有满足\(1 \le x < y \le n\)的二元组按字典序排序,其中第\(i\)个记为\((x_i, y_i)\)。

现在给定一个长度为\(n\)的数组\(a_i = i\),给定\(L, R\),对于\(i = L \dots R\),依次对换\(a_{x_i}和a_{y_i}\)的值。

输出操作完之后的\(a\)。

思路1

对于二元组\((x_i, y_i)\),将其放到第\(x_i\)行。

特判需要操作的区间占某一行中间的情况。

对于剩余情况,可以发现需要操作的二元组可以分成3个部分:第\(x\)行末尾,第\(x+1\)行到\(y-1\)行全部,和第\(y\)行的开头。

观察1:如果第\(i\)行的二元组都操作,相当于将\(a_i,\dots a_n\)循环右移。有\(O(n)\)行的二元组满足行中二元组都要操作。

观察2:剩余操作至多占两行,也就是\(O(n)\)个操作。

借助平衡树维护区间信息,两个操作都可以单次\(O(\log n)\)搞,总的时间复杂度为\(O(n \log n)\)。

思路2

在思路1的基础上进行优化。

观察3:第\(x+1\)行到\(y-1\)行全都操作,相当于翻转\(a_{x+1} \dots a_n\),然后再翻转\(a_{y} \dots a_n\)。

根据这个就可以\(O(n)\)搞了,第\(x\)行末尾暴力搞,第\(x+1\)行到\(y-1\)行的操作拆成了两次翻转,第\(y\)行开头暴力搞,总的时间复杂度为\(O(n)\)。

思路1 AC代码
// Problem: G - Swap Many Times
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

std::mt19937 rng(std::chrono::steady_clock::Now().time_since_epoch().count());
template <typename T>
struct Treap {
  struct node {
    node *l, *r;
    unsigned rnd;
    T v;
    int sz;
    node(T _v) : l(NULL), r(NULL), rnd(rng()), sz(1), v(_v) {}
  };

  inline int get_size(node*& p) { return p ? p->sz : 0; }

  inline void push_up(node*& p) {
    if (!p)
      return;
    p->sz = get_size(p->l) + get_size(p->r) + 1;
  }

  node* root = NULL;

  node* merge(node* a, node* b) {
    if (!a)
      return b;
    if (!b)
      return a;
    if (a->rnd < b->rnd) {
      a->r = merge(a->r, b);
      push_up(a);
      return a;
    } else {
      b->l = merge(a, b->l);
      push_up(b);
      return b;
    }
  }

  void split_val(node* p, const T& k, node*& a, node*& b) {
    if (!p)
      a = b = NULL;
    else {
      if (p->v <= k) {
        a = p;
        split_val(p->r, k, a->r, b);
        push_up(a);
      } else {
        b = p;
        split_val(p->l, k, a, b->l);
        push_up(b);
      }
    }
  }

  void split_size(node* p, int k, node*& a, node*& b) {
    if (!p)
      a = b = NULL;
    else {
      if (get_size(p->l) < k) {
        a = p;
        split_size(p->r, k - get_size(p->l) - 1, a->r, b);
        push_up(a);
      } else {
        b = p;
        split_size(p->l, k, a, b->l);
        push_up(b);
      }
    }
  }

  void ins(int p, T val) {
    node *a, *b;
    split_size(root, p - 1, a, b);
    a = merge(a, new node(val));
    root = merge(a, b);
  }

  void del(T val) {
    node *a, *b, *c, *d;
    split_val(root, val, a, b);
    split_val(a, val - 1, c, d);
    node* e = d;
    d = merge(d->l, d->r);
    delete e;
    a = merge(c, d);
    root = merge(a, b);
  }

  T qry(int p) {
    node *a, *b, *c, *d;
    split_size(root, p - 1, a, b);
    split_size(b, 1, c, d);
    T result = c->v;
    b = merge(c, d);
    root = merge(a, b);
    return result;
  }

  void right_rotate(int l, int r) {
    node *a, *b, *c, *d;
    split_size(root, l - 1, a, b);
    split_size(b, r - l + 1, c, d);

    {
      node *e, *f;
      split_size(c, r - l, e, f);
      c = merge(f, e);
    }

    b = merge(c, d);
    root = merge(a, b);
  }

  void swap(int l, int r) {
    node *a, *b, *c, *d;
    split_size(root, l - 1, a, b);
    split_size(b, r - l + 1, c, d);

    {
      node *e, *f;
      split_size(c, r - l, e, f);

      node *g, *h;
      split_size(e, 1, g, h);

      e = merge(f, h);
      c = merge(e, g);
    }

    b = merge(c, d);
    root = merge(a, b);
  }

  void debug() {
#ifdef BACKLIGHT
    std::function<void(node*)> dfs = [&](node* p) {
      if (!p)
        return;
      dfs(p->l);
      std::cerr << to_string(p->v) << " ";
      dfs(p->r);
    };
    dfs(root);
    std::cerr << std::endl;
#endif
  }
};

void solve_case(int Case) {
  int n;
  std::cin >> n;

  i64 l, r;
  std::cin >> l >> r;

  Treap<int> t;
  for (int i = 1; i <= n; ++i)
    t.ins(i, i);
  t.debug();

  i64 L = 1, R = 0;
  int len = n - 1;
  for (int i = 1; i <= n - 1; ++i) {
    L = R + 1;
    R = L + len - 1;
    --len;

    if (R < l)
      continue;
    if (L > r)
      continue;

    if (L >= l && R <= r) {
      t.right_rotate(i, n);
      logd("S", i);
      t.debug();
      continue;
    }

    int j = i + 1;
    i64 y = L;
    while (y < l) {
      ++j;
      ++y;
    }
    while (y <= r && y <= R) {
      t.swap(i, j);
      ++j;
      ++y;
    }
  }

  for (int i = 1; i <= n; ++i)
    std::cout << t.qry(i) << " \n"[i == n];
}

思路2 AC代码
// Problem: G - Swap Many Times
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif

using i64 = int64_t;
using u64 = uint64_t;

void solve_case(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  // std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    solve_case(t);
  }
  return 0;
}

void solve_case(int Case) {
  int n;
  std::cin >> n;

  i64 l, r;
  std::cin >> l >> r;

  std::vector<int> a(n + 1);
  for (int i = 1; i <= n; ++i)
    a[i] = i;

  i64 L = 1, R = 0;
  int len = n - 1;
  int st = -1, ed = -1, flag = 0;
  for (int i = 1; i <= n - 1; ++i) {
    L = R + 1;
    R = L + len - 1;
    --len;

    if (R < l)
      continue;
    if (L > r)
      continue;

    if (L >= l && R <= r) {
      if (st == -1)
        st = i;
      ed = i;
      logd(st, ed);
      continue;
    }

    if (flag == 0 && st != -1) {
      std::reverse(a.begin() + st, a.end());
      std::reverse(a.begin() + ed + 1, a.end());
      flag = 1;
    }

    int j = i + 1;
    i64 y = L;
    while (y < l) {
      ++j;
      ++y;
    }
    while (y <= r && y <= R) {
      std::swap(a[i], a[j]);
      ++j;
      ++y;
    }
  }
  if (flag == 0 && st != -1) {
    std::reverse(a.begin() + st, a.end());
    std::reverse(a.begin() + ed + 1, a.end());
    flag = 1;
  }

  for (int i = 1; i <= n; ++i)
    std::cout << a[i] << " \n"[i == n];
}

Ex - We Love Forest

TBA。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐