区域赛

23 icpc / ccpc

23 icpc 济南

The 2023 ICPC Asia Jinan Regional Contest (The 2nd Universal Cup. Stage 17: Jinan)

K - Rainbow Subarray

转换条件 $a_{i + 1} - a_i = 1\to a_{i + 1} - (i + 1) = a_i - i$

题目转化为 k 次修改使得一段连续相等的区间最长

有一个结论是要想通过加一减一操作使得一段区间相等, 最小代价是转化为这段区间中位数

使用对顶堆动态维护出中位数再滑动窗口维护即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

namespace Set {
  const int kInf = INT_MAX / 2;
  i64 sum1, sum2;
  int sz1, sz2;
  multiset<int> less, greater;
  void init() {
    sum1 = sum2 = sz1 = sz2 = 0;
    less.clear(), greater.clear();
    less.insert(-kInf), greater.insert(kInf);
  }
  void adjust() {
    while (less.size() > greater.size() + 1) {
      multiset<int>::iterator it = (--less.end());
      greater.insert(*it);
      less.erase(it);
      sum1 -= *it; sum2 += *it;
      sz1--; sz2++;
    }
    while (greater.size() > less.size()) {
      multiset<int>::iterator it = greater.begin();
      less.insert(*it);
      greater.erase(it);
      sum1 += *it; sum2 -= *it;
      sz1++; sz2--;
    }
  }
  void add(int val_) {
    if (val_ <= *greater.begin()) {
      sum1 += val_; sz1++;
      less.insert(val_);
    } else {
      sum2 += val_; sz2++;
      greater.insert(val_);
    }
    adjust();
  }
  void del(int val_) {
    multiset<int>::iterator it = less.lower_bound(val_);
    if (it != less.end()) {
      less.erase(it);
      sum1 -= *it;
      sz1--;
    } else {
      it = greater.lower_bound(val_);
      greater.erase(it);
      sum2 -= *it;
      sz2--;
    }
    adjust();
  }
  int get_middle() {
    return *less.rbegin();
  }
}

void solve() {
  i64 n, k; cin >> n >> k;
  vector<int> a(n + 1);
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    a[i] -= i; 
  }
  Set::init();
  auto check = [&]() {
    i64 v = Set::get_middle();
    i64 sum = v * Set::sz1 - Set::sum1 + Set::sum2 - v * Set::sz2;
    return sum <= k;
  };
  i64 ans = 0;
  for (int i = 1, j = 1; i <= n; i++) {
    Set::add(a[i]);
    while (j < i && !check()) {
      Set::del(a[j]);
      j++;
    }
    ans = max(ans, (i64)Set::sz1 + (i64)Set::sz2);
  } cout << ans << endl;
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  int T; cin >> T;
  while (T--) solve();
  return 0;
}

G - Gifts from Knowledge

显然类似于2-sat 建边, 用并查集维护, 答案为$2^{连通块}$

要拆点, 不能直接做, 细节有点多
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long

using i64 = long long;

const int mod = 1e9 + 7;

void solve() {
    int n, m; cin >> n >> m;
    vector<vector<char>> a(n + 1, vector<char>(m + 1));
    vector<vector<int>> vis(m + 1);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
            if (a[i][j] == '1') vis[j].push_back(i);
        }
    }
    vector<int> fa(2 * n + 1);
    for (int i = 1; i <= 2 * n; i++) {
        fa[i] = i;
    }
    function<int(int)> Find = [&](int x) {
        return x == fa[x] ? x : fa[x] = Find(fa[x]);
    };
    for (int i = 1; i <= m; i++) {
        int all = vis[i].size() + vis[m + 1 - i].size();
        if (all <= 1) continue;
        else if (all >= 3) {cout << 0 << endl; return;}
        if (vis[i].size() == 2) {
            int id1 = vis[i][0] + n, id2 = vis[i][1];
            id1 = Find(id1); id2 = Find(id2);
            if (id1 != id2) fa[id2] = id1;

            id1 = vis[i][0], id2 = vis[i][1] + n;
            id1 = Find(id1); id2 = Find(id2);
            if (id1 != id2) fa[id2] = id1;
        } else if (vis[m + 1 - i].size() == 2) {
            int id1 = vis[m + 1 - i][0] + n, id2 = vis[m + 1 - i][1];
            id1 = Find(id1); id2 = Find(id2);
            if (id1 != id2) fa[id2] = id1;
            
            id1 = vis[m + 1 - i][0], id2 = vis[m + 1 - i][1] + n;
            id1 = Find(id1); id2 = Find(id2);
            if (id1 != id2) fa[id2] = id1;
        } else {
            int id1 = vis[m + 1 - i][0], id2 = vis[i][0];
            id1 = Find(id1); id2 = Find(id2);
            if (id1 != id2) fa[id2] = id1;
            
            id1 = vis[m + 1 - i][0] + n, id2 = vis[i][0] + n;
            id1 = Find(id1); id2 = Find(id2);
            if (id1 != id2) fa[id2] = id1;
        }
    }
    auto ksm = [&](i64 a, i64 b) {
        i64 ans = 1 % mod;
        a %= mod;
        while (b) {
            if (b & 1) ans = (ans * a) % mod;
            a = (a * a) % mod;
            b >>= 1;
        }
        return ans;
    };

    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (fa[i] == i) cnt++;
        if (Find(i) == Find(i + n)) {
            cout << 0 << endl;
            return;
        }
    }
    cout << ksm(2, cnt) << endl;
}

signed main() {
    cin.tie(nullptr) -> ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--) solve();
    return 0;
}

23 icpc 沈阳

The 2023 ICPC Asia Shenyang Regional Contest (The 2nd Universal Cup. Stage 13: Shenyang)

E - Sheep Eat Wolves

$f_{i,j,k,0/1}$表示 左羊 左狼 右羊 当前在左/右岸 bfs转移即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

const int N = 105;

int f[N][N][N][2], vis[N][N][N][2];

void solve() {
  int x, y, p, q; cin >> x >> y >> p >> q;
  queue<array<int, 4>> Q;
  memset(f, 127, sizeof f);
  Q.push({x, y, 0, 0});
  f[x][y][0][0] = 0;

  auto check = [&](int l1, int l2) {
    return !l1 || (l1 && l1 + q >= l2);
  };

  int ans = INT_MAX / 2;
  while (!Q.empty()) {
    auto [v1, v2, v3, op] = Q.front(); Q.pop();
    if (vis[v1][v2][v3][op]) continue;
    vis[v1][v2][v3][op] = 1;
    int v4 = y - v2;
    if (v3 == x) {
      ans = min(ans, f[v1][v2][v3][op]);
    }
    for (int i = 0; i <= (op ? v3 : v1); i++) {
      for (int j = 0; j <= (op ? v4 : v2); j++) {
        if (i + j > p) continue;
        if (!op) {
          int l1 = v1 - i, l2 = v2 - j;
          if (check(l1, l2) && !vis[l1][l2][v3 + i][op ^ 1]) {
            Q.push({l1, l2, v3 + i, op ^ 1});
            f[l1][l2][v3 + i][op ^ 1] = min(f[l1][l2][v3 + i][op ^ 1], f[v1][v2][v3][op] + 1);
          }
        } else {
          int l1 = v3 - i, l2 = v4 - j;
          if (check(l1, l2) && !vis[v1 + i][v2 + j][l1][op ^ 1]) {
            Q.push({v1 + i, v2 + j, l1, op ^ 1});
            f[v1 + i][v2 + j][l1][op ^ 1] = min(f[v1 + i][v2 + j][l1][op ^ 1], f[v1][v2][v3][op] + 1);
          }
        }
      }
    }
  }
  cout << (ans == INT_MAX / 2 ? -1 : ans) << endl;
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  solve();
  return 0;
}

K - Maximum Rating

线段树上二分
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long

using i64 = long long;

const int N = 4e5 + 100;

int a[N];
vector<int> vec;

struct node {
  int cnt;
  i64 sum;
} Seg[N * 4];

node operator + (const node &l, const node &r) {
  node ans; ans.cnt = l.cnt + r.cnt;
  ans.sum = l.sum + r.sum;
  return ans;
}

void pushup(int id) {
  Seg[id] = Seg[id * 2] + Seg[id * 2 + 1];
}

void build(int id, int l, int r) {
  if (l == r) {
    Seg[id] = {0, 0};
    return;
  }
  int mid = l + r >> 1;
  build(id * 2, l, mid); build(id * 2 + 1, mid + 1, r);
  pushup(id);
}

void modify(int id, int l, int r, int pos, int v) {
  if (l == r) {
    Seg[id].sum += v * vec[l - 1];
    Seg[id].cnt += v;
    return;
  }
  int mid = l + r >> 1;
  if (pos <= mid) modify(id * 2, l, mid, pos, v);
  else modify(id * 2 + 1, mid + 1, r, pos, v);
  pushup(id);
}

pair<int, int> query1(int id, int l, int r, int v) {
  if (l == r) return {l, Seg[id].cnt};
  int mid = l + r >> 1;
  if (Seg[id * 2].sum > v) return query1(id * 2, l, mid, v);
  else return query1(id * 2 + 1, mid + 1, r, v - Seg[id * 2].sum);
}

node query2(int id, int l, int r, int x, int y) {
  if (x <= l && y >= r) return Seg[id];
  int mid = l + r >> 1;
  node ans = {0, 0};
  if (x <= mid) ans = ans + query2(id * 2, l, mid, x, y);
  if (y > mid) ans = ans + query2(id * 2 + 1, mid + 1, r, x, y);
  return ans;
}

void solve() {
  int n, q; cin >> n >> q;
  vec.clear();
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    vec.push_back(a[i]);
  }

  vector<array<int, 2>> que(q + 1);
  for (int i = 1; i <= q; i++) {
    int x, v; cin >> x >> v;
    que[i] = {x, v};
    vec.push_back(v);
  }

  vec.push_back(-1e9 - 1); vec.push_back(1e9 + 1);
  sort(vec.begin(), vec.end());
  vec.erase(unique(vec.begin(), vec.end()), vec.end());

  auto getid = [&](int x) {
    return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1;
  };

  int m = vec.size(), z = 0;
  i64 res = 0;
  build(1, 1, m);
  for (int i = 1; i <= n; i++) {
    int id = getid(a[i]);
    if (a[i] > 0) z++;
    modify(1, 1, m, id, 1);
    res += a[i];
  }
  for (int i = 1; i <= q; i++) {
    auto [x, v] = que[i];
    int id1 = getid(a[x]), id2 = getid(v);
    res += v - a[x];
    if (a[x] > 0) z--;
    if (v > 0) z++;
    modify(1, 1, m, id1, -1);
    a[x] = v;
    modify(1, 1, m, id2, 1);
    auto [pos, cnt] = query1(1, 1, m, 0);
    auto [num, sum] = query2(1, 1, m, 1, pos);
    // cout << "TEST" << endl;
    // for (int j = 1; j <= n; j++) {
    //  cout << a[j] << " \n"[j == n];
    // }
    // cout << vec[pos - 1] << ' ' << cnt << ' ' << num << ' ' << sum << endl;
    if (res <= 0) {
      cout << z + 1 << '\n';
      continue;
    }
    int L = 0, R = cnt;
    auto check = [&](int V) {
      return sum - V * vec[pos - 1] > 0;
    };
    while (L + 1 < R) {
      int M = L + R >> 1;
      if (check(M)) L = M;
      else R = M;
    }
    int p = check(R) ? R : L;
    p = num - p + 1;
    cout << z - (n - p + 1) << '\n';
    // cout << "TEST " << i << ' ' << z << endl;
  }
}

signed main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  solve();
  return 0;
}

2023 icpc 杭州

The 2023 ICPC Asia Hangzhou Regional Contest (The 2nd Universal Cup. Stage 22: Hangzhou)

H - Sugar Sweet II

需要观察到必然不发生的边直接断开即可, 每个点去找离他最近的必然发生的边, 跑个多源最短路

设当前点 x 最近的必然发生边长度为 L, 则该点事件发生概率为$\frac{1}{L!}$
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long

using i64 = long long;

const int N = 5e5 + 100;
const int mod = 1e9 + 7;

vector<int> g[N];
int a[N], b[N], w[N];

void solve() {
  int n; cin >> n;
  for (int i = 1; i <= n; i++) cin >> a[i];
  for (int i = 1; i <= n; i++) cin >> b[i];
  for (int i = 1; i <= n; i++) cin >> w[i];
  vector<int> tmp;
  for (int i = 1; i <= n; i++) {
    int l = a[b[i]], r = a[b[i]] + w[b[i]];
    if (a[i] >= r) {
      continue;
    } else if (a[i] < l) {
      g[b[i]].push_back(i);
      tmp.push_back(i);
    } else {
      g[b[i]].push_back(i);
    }
  }
  vector<int> dist(n + 1, 1e9), vis(n + 1);
  priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
  for (auto i : tmp) {
    dist[i] = 0;
    q.push({0, i});
  }
  while (!q.empty()) {
    auto [v, x] = q.top(); q.pop();
    if (vis[x]) continue;
    vis[x] = 1;
    for (auto y : g[x]) {
      if (vis[y]) continue;
      if (dist[y] > dist[x] + 1) {
        dist[y] = dist[x] + 1;
        q.push({dist[y], y});
      }
    }
  }
  auto ksm = [&](i64 a, i64 b) {
    i64 ans = 1 % mod;
    a %= mod;
    while (b) {
      if (b & 1) ans = (ans * a) % mod;
      a = (a * a) % mod;
      b >>= 1;
    }
    return ans;
  };
  vector<int> p(n + 1);
  int v = 1;
  for (int i = 1; i <= n; i++) {
    v *= i; v %= mod;
    p[i] = ksm(v, mod - 2);
  }
  // cout << "TEST" << endl;
  for (int i = 1; i <= n; i++) {
    // cout << i << ' ' << dist[i] << endl;
    int ans = a[i];
    if (dist[i] < (int)1e9) {
      ans = (ans + p[dist[i] + 1] * w[i] % mod) % mod;
    }
    cout << ans << " \n"[i == n];
  }
  for (int i = 1; i <= n; i++) {
    g[i].clear();
  }
}

signed main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  int T; cin >> T;
  while (T--) {
    solve();
  }
  return 0;
}
 

G - Snake Move

需要观察出结论不在蛇身的点直接最短路跑就行, 在蛇身需要到他最短距离加上缩尾的代价
点击查看代码
#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using u64 = unsigned long long;

const int N = 3005;

char a[N][N];
int X[N * 100], Y[N * 100], bel[N][N], dist[N][N], vis[N][N];
int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};

void solve() {
  int n, m, k; cin >> n >> m >> k;
  for (int i = 1; i <= k; i++) {
    cin >> X[i] >> Y[i];
    bel[X[i]][Y[i]] = i;
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      cin >> a[i][j];
    }
  }
  memset(dist, 127, sizeof dist);
  priority_queue<array<int, 3>, vector<array<int, 3>>, greater<array<int, 3>>> q;
  dist[X[1]][Y[1]] = 0;
  q.push({0, X[1], Y[1]});
  while (!q.empty()) {
    auto [v, x, y] = q.top(); q.pop();
    if (vis[x][y]) continue;
    vis[x][y] = 1;
    for (int i = 0; i < 4; i++) {
      int nx = x + dir[i][0];
      int ny = y + dir[i][1];
      if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && a[nx][ny] != '#' && !vis[nx][ny]) {
        if (bel[nx][ny]) {
          int del = k - bel[nx][ny];
          if (dist[x][y] >= del) {
            if (dist[x][y] + 1 < dist[nx][ny]) {
              dist[nx][ny] = dist[x][y] + 1;
              q.push({dist[nx][ny], nx, ny});
            }
          } else {
            if (dist[x][y] + 1 + del - dist[x][y] < dist[nx][ny]) {
              dist[nx][ny] = dist[x][y] + 1 + del - dist[x][y];
              q.push({dist[nx][ny], nx, ny});
            }
          }
        } else {
          if (dist[x][y] + 1 < dist[nx][ny]) {
            dist[nx][ny] = dist[x][y] + 1;
            q.push({dist[nx][ny], nx, ny});
          }
        }
      }
    }
  }
  u64 ans = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (dist[i][j] < 1 << 30) {
        ans += (u64)dist[i][j] * dist[i][j];
        // cout << dist[i][j] << " \n"[j == n];
      }
    }
  }
  cout << ans << endl;
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  solve();
  return 0;
}

23 icpc 合肥

The 2023 ICPC Asia Hefei Regional Contest (The 2nd Universal Cup. Stage 12: Hefei)

J. Takeout Delivering

先对 $1$ 和 $n$ 跑 $Dijkstra$, 枚举每条边作为最大边计算贡献即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

const int N = 3e5 + 100;

int n, m;
vector<array<int, 2>> g[N];

vector<int> Dijkstra(int s) {
  vector<int> vis(n + 10), dist(n + 10, 2e9);
  priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
  q.push({0, s});
  dist[s] = 0; 
  while (q.size()) {
    auto [v, x] = q.top(); q.pop();
    if (vis[x]) continue;
    vis[x] = 1;
    for (auto [y, val] : g[x]) {
      if (vis[y]) continue;
      if (max(dist[x], val) < dist[y]) {
        dist[y] = max(dist[x], val);
        q.push({dist[y], y});
      }
    }
  }
  return dist;
}

void solve() {
  cin >> n >> m;
  vector<array<int, 3>> e(m + 10);
  for (int i = 1; i <= m; i++) {
    int x, y, v; cin >> x >> y >> v;
    g[x].push_back({y, v});
    g[y].push_back({x, v});
    e[i] = {x, y, v};
  }
  auto d1 = Dijkstra(1);
  auto d2 = Dijkstra(n);
  int ans = 2e9;
  for (int i = 1; i <= m; i++) {
    auto [x, y, v] = e[i];
    int t = min(max(d1[x], d2[y]), max(d2[x], d1[y]));
    if (t <= v) ans = min(ans, t + v);
  } cout << ans << endl;
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  solve();
  return 0;
}

J. Takeout Delivering

二分答案然后dp

$f_{i,j}$ 表示到第 i 个划分出来的 (>= mid) 段最小需要多少代价

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

void solve() {
  int n, m, k; cin >> n >> m >> k;
  string s; cin >> s;
  s = " " + s;
  vector<int> pre(n + 1);
  for (int i = 1; i <= n; i++) {
    pre[i] = pre[i - 1] + (s[i] == '0');
  }
  auto check = [&](int x) {
    auto V = [&](int l, int r) {
      return pre[r] - pre[l - 1];
    };
    vector<vector<int>> f(n + 1, vector<int>(k + 1, 1e9));
    f[0][0] = 0;
    for (int i = 1; i <= n; i++) {
      f[i] = f[i - 1];
      for (int j = 1; j <= k; j++) {
        if (i - x >= 0 && s[i - x] != '1') {
          f[i][j] = min(f[i][j], f[max(i - x - 1, 0)][j - 1] + V(i - x + 1, i));
        }
      }
    }
    return f[n][k] <= m;
  };
  int l = 1, r = n;
  while (l + 1 < r) {
    int mid = l + r >> 1;
    if (check(mid)) l = mid;
    else r = mid;
  } cout << (check(r) ? r : (check(l) ? l : -1)) << endl; 
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  solve();
  return 0;
}

23 ccpc 桂林

2023 China Collegiate Programming Contest (CCPC) Guilin Onsite (The 2nd Universal Cup. Stage 8: Guilin))

K. Randias Permutation Task
$n*m <= 180 $ 是一个很强的限制条件

当数组长度大于 $9$ 时, 不会超过 20 个这样的数组, 这时我们二进制枚举即可

当数组长度小于 $9$ 时, 直接暴力, 小于 9 的数组最多也就 $9\space!$ 种

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

void solve() {
  int n, m; cin >> m >> n;
  vector<vector<int>> a(n + 1, vector<int>(m + 1));
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      cin >> a[i][j];
    }
  }
  if (n <= 20) {
    unordered_set<string> vis;
    for (int tot = 1; tot < (1 << n); tot++) {
      string s = " ";
      for (int i = 1; i <= n; i++) {
        if (tot >> (i - 1) & 1) {
          if (s.size() == 1) {
            for (int j = 1; j <= m; j++) {
              s += char(a[i][j] + '0');
            }
          } else {
            string t = s;
            for (int j = 1; j <= m; j++) {
              s[j] = t[a[i][j]];
            }
          }
        }
      }
      vis.insert(s);
    }
    cout << int(vis.size()) << endl;
  } else {
    unordered_set<string> vis;
    for (int i = 1; i <= n; i++) {
      string s = " ";
      for (int j = 1; j <= m; j++) {
        s += char(a[i][j] + '0');
      }
      vector<string> vec;
      for (auto t : vis) {
        string tmp = t;
        for (int j = 1; j <= m; j++) {
          tmp[j] = t[a[i][j]];
        }
        vec.push_back(tmp);
        // vis.insert(tmp);
      }
      for (auto k : vec) vis.insert(k);
      vis.insert(s);
    }
    cout << int(vis.size()) << endl;
  }
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  solve();
  return 0;
}
I. Barkley II

要求 $allcol - mex$ 我们枚举 $mex$, 设 $a_i$ 上次出现位置为 $lst$, 那么区间 $[lst, i]$ $mex$ 一定是小于等于 $a_i$ 的, 我们枚举出来所有这样的区间 $mex$, 这样的答案一定是不劣的, 现在问题就变成给定了一些区间求这些区间内有多少种颜色, 就变成了HH的项链原题, 很坑的一点是要对整个区间再计算一遍 $allcol - mex$

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

const int N = 1e6 + 100;

int Seg[N * 4];
void pushup(int id) {
  Seg[id] = Seg[id * 2] + Seg[id * 2 + 1];
}

void build(int id, int l, int r) {
  if (l == r) {
    Seg[id] = 0;
    return;
  }
  int mid = l + r >> 1;
  build(id * 2, l, mid); build(id * 2 + 1, mid + 1, r);
  pushup(id);
}

void modify(int id, int l, int r, int pos, int v) {
  if (l == r) {
    Seg[id] = v;
    return;
  }
  int mid = l + r >> 1;
  if (pos <= mid) modify(id * 2, l, mid, pos, v); 
  else modify(id * 2 + 1, mid + 1, r, pos, v);
  pushup(id);
}

int query(int id, int l, int r, int x, int y) {
  if (x <= l && y >= r) return Seg[id];
  int mid = l + r >> 1, ans = 0;
  if (x <= mid) ans += query(id * 2, l, mid, x, y);
  if (y > mid) ans += query(id * 2 + 1, mid + 1, r, x, y);
  return ans; 
}

void solve() {
  int n, m; cin >> n >> m;
  vector<int> a(n + 1);
  map<int, vector<int>> pos;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    pos[a[i]].push_back(i);
  }
  vector<vector<array<int, 2>>> que(n + 1);
  for (auto &[v, vec] : pos) {
    vector<int> tmp;
    if (vec.front() != 1) tmp.push_back(0);
    for (auto i : vec) tmp.push_back(i);
    if (vec.back() != n) tmp.push_back(n + 1);
    for (int i = 1; i < tmp.size(); i++) {
      int l = tmp[i - 1] + 1, r = tmp[i] - 1;
      if (l <= r) que[r].push_back({l, v});
    }
  }
  build(1, 1, n);
  map<int, int> lst;
  int ans = INT_MIN / 2;
  for (int i = 1; i <= n; i++) {
    modify(1, 1, n, i, 1);
    if (lst[a[i]]) modify(1, 1, n, lst[a[i]], 0);
    lst[a[i]] = i;
    for (auto [l, v] : que[i]) {
      // cout << l << ' ' << i << endl;
      ans = max(ans, query(1, 1, n, l, i) - v);
    }
  } 
  int mex = 1;
  while (lst.count(mex)) mex++;
  ans = max(ans, (int)lst.size() - mex);
  cout << ans << endl;
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  int T; cin >> T;
  while (T--) solve();
  return 0;
}
B. The Game

一定是 $a$ 数组中最大 $m$ 个数变成 $b$ 数组的数, 题解挺详细的 其中有个细节导致一直 $re$

正确写法:

    auto it = s1.begin(); 
    s1.erase(s1.begin());
    int x = *it;
    ans.push_back(x);

错误写法

    auto it = s1.begin(); 
    s1.erase(s1.begin());
    ans.push_back(*it);

疑似是因为erase后 it指针会指到另外一个数上
好像写了点 ub 的东西

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

#define int i64

const int N = 3e5 + 100;

int a[N], b[N];

void solve() {
  int n, m; cin >> n >> m;
  for (int i = 1; i <= n; i++)  
    cin >> a[i];
  for (int i = 1; i <= m; i++) 
    cin >> b[i];
  
  sort(a + 1, a + 1 + n);
  sort(b + 1, b + 1 + m);
  
  i64 sum = 0;
  for (int i = 1; i <= m; i++) {
    if (a[n - (m - i)] > b[i]) {
      cout << -1 << endl;
      return;
    }
    sum += b[i] - a[n - (m - i)];
  }
  if (sum > n - m) {
    cout << -1 << endl; 
    return;
  }
  multiset<int> s1, s2;
  for (int i = 1; i <= n - m; i++) 
    s1.insert(a[i]);
  for (int i = n - m + 1; i <= n; i++) 
    s2.insert(a[i]);
  vector<int> ans;
  while (n - m - sum > 0) {
    if (s1.empty()) {
      cout << -1 << endl;
      return;
    }
    auto it = s1.begin(); 
    int x = *it;
    s1.erase(s1.begin());
    ans.push_back(x);
    x++;
    if (int(s2.size()) < m || x > *s2.begin()) {
      sum--;
      s1.insert(x - 1);
      s2.insert(x);
      s2.erase(s2.begin());
    } else {
      s1.insert(x);
    }
    if (s1.size()) s1.erase(s1.begin());
    else s2.erase(s2.begin());
    n--;
  }
  for (int i = 1; i <= m; i++) {
    if (!s2.size()) {
      cout << -1 << endl;
      return;
    }
    int x = *s2.begin();
    s2.erase(s2.begin());
    if (x > b[i]) {
      cout << -1 << endl;
      return;
    }
    while (x < b[i] && s1.size()) {
      ans.push_back(x);
      x++;
      s1.erase(s1.begin());
    }
    if (x < b[i]) {
      cout << -1 << endl;
      return;
    }
  }
  cout << int(ans.size()) << endl;
  for (int i = 0; i < ans.size(); i++) 
    cout << ans[i] << " \n"[i == (int)ans.size() - 1];
}

signed main() {
  cin.tie(0) -> ios::sync_with_stdio(false);
  int T; cin >> T;
  while (T--) solve();
  return 0;
}

C. Master of Both IV

网络赛出了个线性基板子题, 之前学就写了个模板题, 回旋镖了
首先一个显然的转换是\(\forall x\in[1,m],a_{i_x}|\bigoplus\limits_{j=1}^m a_{i_j}\to lcm|\bigoplus\limits_{j=1}^m a_{i_j}\)

之后感觉非常深刻,我们令选择出来的子序列中最大值为 \(maxv\)

\(\bigoplus\limits_{j=1}^m a_{i_j} < 2*maxv\)

\(lcm >= k * maxv(k >= 1)\)

那么显然 $\bigoplus\limits_{j=1}^m a_{i_j} $取值的范围就只有 \(0\)\(maxv\)

\(\bigoplus\limits_{j=1}^m a_{i_j} = s\)

  • \(s = maxv\)

23 icpc 南京

The 2023 ICPC Asia Nanjing Regional Contest (The 2nd Universal Cup. Stage 11: Nanjing)

G. Knapsack
显然的一件事情是当我们选出来一些宝石后,我们肯定是对最贵的 \(k\) 个宝石免费
那么我们只需要将宝石按照价格排序, 枚举每个位置,对前缀做背包,后缀贪心处理出选 \(k\) 个能得到的最大美观度是多少即可

代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

const int N = 1e4 + 10;

void solve() {
  int n, m, k; cin >> n >> m >> k;
  vector<array<int, 2>> a(n + 1);
  for (int i = 1; i <= n; i++) {
    cin >> a[i][0] >> a[i][1];
  }
  sort(a.begin() + 1, a.end());
  i64 sum = 0;
  vector<i64> suf(n + 2), f(m + 2);
  priority_queue<int, vector<int>, greater<int>> q;
  for (int i = n; i >= 1; i--) {
    if (q.size() < k) {
      q.push(a[i][1]);
      sum += a[i][1];
    } else {
      q.push(a[i][1]);
      sum += a[i][1] - q.top();
      q.pop();
    }
    suf[i] = sum;
  } 
  i64 ans = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = m; j >= a[i][0]; j--) {
      f[j] = max(f[j], f[j - a[i][0]] + a[i][1]);
      ans = max(ans, f[j] + suf[i + 1]);
    }
  } cout << ans << endl;
}

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  solve();
  return 0;
}

F. Equivalent Rewriting
很显然的拓扑, 但是题解的构造方案似乎复杂了, 我们只需要拿优先队列按编号从大到小输出方案即可(若与原来一样则输出 \(NO\))

代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

void solve() {
  int n, m; cin >> n >> m;
  vector<vector<int>> p(m + 1);
  for (int i = 1; i <= n; i++) {
    int sz; cin >> sz;
    for (int j = 1; j <= sz; j++) {
      int x; cin >> x;
      p[x].push_back(i);
    }
  }
  vector<int> inc(n + 1);
  vector<vector<int>> g(n + 1);
  map<pair<int, int>, int> vis;
  for (int i = 1; i <= m; i++) {
    for (int j = 0; j < int(p[i].size()) - 1; j++) {
      int x = p[i][j], y = p[i].back();
      if (!vis[{x, y}]) {
        vis[{x, y}] = 1;
        g[x].push_back(y);
        inc[y]++;
      }
    }
  }
  priority_queue<int> q;
  for (int i = 1; i <= n; i++) {
    if (!inc[i]) q.push(i);
  }
  vector<int> ans;
  while (q.size()) {
    auto x = q.top(); q.pop();
    ans.push_back(x);
    for (auto y : g[x]) {
      if (--inc[y] == 0) q.push(y);
    }
  }
  for (int i = 1; i <= n; i++) {
    if (inc[i]) {
      cout << "No" << endl;
      return;
    }
  }
  bool ok = false;
  for (int i = 0; i < n; i++) {
    if (ans[i] != i + 1) ok = true;
  }
  if (ok) {
    cout << "Yes" << endl;
    for (int i = 1; i <= n; i++) {
      cout << ans[i - 1] << " \n"[i == n]; 
    }
  } else {
    cout << "No" << endl;
  }
} 

int main() {
  cin.tie(nullptr) -> ios::sync_with_stdio(false);
  int T; cin >> T;
  while (T--) solve();
  return 0;
}

A.Cool, It's Yesterday Four Times More
需要观察到每个连通块的袋鼠其实是等价的,所以我们暴力的判断某个连通块内是否会被其他连通块袋鼠的操作移除即可

代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"

using i64 = long long;

void solve() {
  int n, m; cin >> n >> m;
  vector<vector<char>> a(n + 1, vector<char>(m + 1));
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      cin >> a[i][j];
    } 
  }

  int nowcol = 0;
  int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
  vector<vector<int>> vis(n + 1, vector<int>(m + 1));
  auto check = [&](int x, int y) {
    return x >= 1 && x <= n && y >= 1 && y <= m && a[x][y] == '.';
  };
  
  vector<pair<int, int>> tmp;
  auto bfs = [&](int X, int Y) {
    queue<pair<int, int>> q;
    q.push({X, Y});
    nowcol++;
    while (!q.empty()) {
      auto [x, y] = q.front(); q.pop();
      if (vis[x][y]) continue;
      vis[x][y] = nowcol;
      tmp.push_back({x - X, y - Y});
      for (int i = 0; i < 4; i++) {
        int nx = x + dir[i][0];
        int ny = y + dir[i][1];
        if (check(nx, ny) && !vis[nx][ny]) {
          q.push({nx, ny});
        }
      } 
    }
  };

  i64 ans = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (vis[i][j] || a[i][j] != '.') continue;
      tmp.clear();
      bfs(i, j);
      bool jud = false;
      for (int x = 1; x <= n; x++) {
        for (int y = 1; y <= m; y++) {
          bool ok = false;
          if (nowcol == vis[x][y] || a[x][y] != '.') continue;
          for (auto [delx, dely] : tmp) {
            if (!check(x + delx, y + dely)) {
              ok = true;
              break;
            }
          }
          if (!ok) {jud = true; break;}
        }
        // if (jud) break;
      }
      if (!jud) ans += tmp.size();
    }
  } cout << ans << endl;
}

signed main() {
  cin.tie(0) -> ios::sync_with_stdio(false);
  int T; cin >> T;
  while (T--) solve();
  return 0;
}

23 ccpc 哈尔滨

The 9th CCPC (Harbin) Onsite(The 2nd Universal Cup. Stage 10: Harbin)

L. Palm Island

感觉很巧妙的一题,假设我们现在有一个指针时刻指着第一个元素,这个元素位置为 \(p\)
操作一就转化为指针右移
操作二就转化为交换 \(p\)\(p + 1\) 并且指针当前位置还是指着原来那个元素的
然后就会发现这个过程与冒泡排序基本上一模一样, 对元素位置做一个映射,再模拟冒泡排序即可

代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

void solve() {
    int n; cin >> n;
    vector<int> a(n), b(n), c(n);
    map<int, int> p;
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < n; i++) {
        cin >> b[i];
        p[b[i]] = i;
    }
    for (int i = 0; i < n; i++) {
        c[i] = p[a[i]];
    }
    string ans = "";
    int now = 0;
    // op1 -> 指针右移
    // op2 -> 指针指着的元素不变和他下个元素位置交换 
    while (true) {
        if (!now) {
            int ok = 0;
            for (int i = 0; i < n - 1; i++) {
                ok |= c[i] > c[i + 1];
            }
            if (!ok) break;
        }
        if (now < n - 1 && c[now] > c[now + 1]) {
            swap(c[now], c[now + 1]);
            now++;
            ans += '2';
        } else {
            now++;
            ans += '1';
        }
        now %= n;
    }
    cout << ans << endl;
}

int main() {
    cin.tie(nullptr) -> ios::sync_with_stdio(false);
    int T; cin >> T;
    while (T--) solve();
    return 0;
}

G. The Only Way to the Destination

J. Game on a Forest

23 ccpc 秦皇岛

The 2023 CCPC (Qinhuangdao) Onsite (The 2nd Universal Cup. Stage 9: Qinhuangdao)

J. Keyi LIkes Reading

D. Yet Another Coffee

F. Mystery of Prime

posted @ 2024-09-03 16:11  zhujio  阅读(75)  评论(0)    收藏  举报