20240925 随机训练

Yukicoder 2897

题目描述

给定两个点集 \(S,T\),我们定义 \(d((x_1,y_1),(x_2,y_2))=|x_1-x_2|+|y_1-y_2|\)

我们定义 两个集合 \(S,T\) 的距离 \(D(S,T)=\min \limits_{s\in S,t\in T}\{d(s,t)\}\)。求 \(D(S,T)\)

思路

我们把每个 \(S\) 中的元素放在一起做一个多源 bfs,然后对每个 \(T\) 中的元素看它的最短路即可。

时空复杂度均为 \(O(V^2)\)

代码

#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;

const int MAXN = 200001, MAXV = 1000, dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};

int n, m, dist[MAXV][MAXV], ans = int(1e9);
bool vis[MAXV][MAXV];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n;
  queue<pii> que;
  for(int i = 1, x, y; i <= n; ++i) {
    cin >> x >> y;
    vis[x][y] = 1;
    dist[x][y] = 0;
    que.push({x, y});
  }
  for(; !que.empty(); ) {
    auto [x, y] = que.front();
    que.pop();
    for(int d : {0, 1, 2, 3}) {
      int nx = x + dx[d], ny = y + dy[d];
      if(nx >= 0 && nx < 1000 && ny >= 0 && ny < 1000 && !vis[nx][ny]) {
        vis[nx][ny] = 1;
        dist[nx][ny] = dist[x][y] + 1;
        que.push({nx, ny});
      }
    }
  }
  cin >> m;
  for(int i = 1, x, y; i <= m; ++i) {
    cin >> x >> y;
    ans = min(ans, dist[x][y]);
  }
  cout << ans;
  return 0;
}

Yukicoder 2899

题目描述

给定一个 \(01\)\(S\),求有多少个长度为 \(N\) 的排列 \(p\) 满足以下条件:

  • 对于每个 \(1\le i<j\le N且 S_i=0,S_j=1\),都要满足 \(p_i<p_j\)

思路

我们定义 \(dp_{i,j}\) 表示当前考虑到第 \(i\) 个,下一个 \(1\) 的方案数为 \(j\) 的方案数。

如果 \(S_{i+1}=1\),那么明显 \(dp_{i+1,j-1}\leftarrow j\cdot dp_{i,j}\)

否则,此时剩余的数有 \(N-i\) 个,其中有 \(N-i-j\) 个数只能用在 \(0\) 上,也就是 \(dp_{i+1,j}\leftarrow (N-i-j)\cdot dp_{i,j}\)。而可以用在 \(1\) 上的 \(j\) 个数,那么如果你当前用了第 \(k\) 大的,那么第 \(k,k+1,\dots,j\) 大的都不能用了(因为他们比当前这个小)。所以我们有转移 \(dp_{i+1,j-k}\leftarrow dp_{i,j}(1\le k\le j)\)。这个可以用差分优化。

时空复杂度均为 \(O(N^2)\)

代码

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

const int MAXN = 2001, MOD = 998244353;

int n, dp[MAXN][MAXN], sum[MAXN][MAXN];
string s;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> s;
  s = ' ' + s;
  dp[0][n] = 1;
  for(int i = 0; i <= n; ++i) {
    for(int j = n - i; j >= 0; --j) {
      sum[i][j] = (sum[i][j] + sum[i][j + 1]) % MOD;
      dp[i][j] = (dp[i][j] + sum[i][j]) % MOD;
      if(i == n) {
        continue;
      }
      if(s[i + 1] == '1' && j) {
        dp[i + 1][j - 1] = (dp[i + 1][j - 1] + 1ll * j * dp[i][j] % MOD) % MOD;
      }else if(s[i + 1] == '0') {
        dp[i + 1][j] = (dp[i + 1][j] + 1ll * dp[i][j] * (n - i - j) % MOD) % MOD;
        if(j) {
          sum[i + 1][j - 1] = (sum[i + 1][j - 1] + dp[i][j]) % MOD;
        }
      }
    }
  }
  cout << dp[n][0];
  return 0;
}

Yukicoder 2901

题目描述

你有一个长度为 \(N\) 的序列 \(A\),有 \(Q\) 次操作:

  • \(A_p\leftarrow x\)
  • 判断 \([l,r]\) 是否存在字串使得其按位或为 \(2^K-1\),如果有,输出最短长度。

思路

使用线段树维护。

线段树中我们记录这些信息:当前区间答案,前/后缀的使前/后缀按位或发生变化的位置和此时的按位或和。具体来说,就是:

  • \(sum_i=A_1\operatorname{OR}A_2\operatorname{OR}\dots A_i\)。我们会记录 \(sum_{i-1}\ne sum_i\)\((sum_i,i)\)

我们在合并信息时,当前 \(区间答案=\min\{左区间答案,右区间答案,中间的答案\}\)。这里中间的答案就是左区间的后缀+右区间的前缀。

因为我们记录的前/后缀每次变化至少让一位 \(0\rightarrow 1\),所以至多变化 \(K\) 次。所以求中间的答案是 \(O(K^2)\) 的。

空间复杂度 \(O(NK)\),时间复杂度 \(O(QK\log N)\)

代码

#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;

const int MAXN = 200001;

int k;

struct INFO {
  int ans, len;
  vector<pii> pre, suf;
  INFO operator+(const INFO &x) {
    INFO ret;
    ret.ans = min(ans, x.ans);
    ret.len = len + x.len;
    for(auto [v, l] : suf) {
      for(auto [v2, l2] : x.pre) {
        if((v | v2) == (1 << k) - 1) {
          ret.ans = min(ret.ans, l + l2);
        }
      }
    }
    ret.pre = pre;
    for(auto [v, l] : x.pre) {
      if(((ret.pre.empty() ? 0 : ret.pre.back().first) | v) != (ret.pre.empty() ? 0 : ret.pre.back().first)) {
        ret.pre.emplace_back(((ret.pre.empty() ? 0 : ret.pre.back().first) | v), len + l);
      }
    }
    ret.suf = x.suf;
    for(auto [v, l] : suf) {
      if(((ret.suf.empty() ? 0 : int(ret.suf.back().first)) | v) != (ret.suf.empty() ? 0 : ret.suf.back().first)) {
        ret.suf.emplace_back(((ret.suf.empty() ? 0 : ret.suf.back().first) | v), x.len + l);
      }
    }
    return ret;
  }
};

struct Segment_Tree {
  int l[MAXN << 2], r[MAXN << 2], a[MAXN];
  INFO v[MAXN << 2];
  void build(int u, int s, int t) {
    l[u] = s, r[u] = t;
    if(s == t) {
      v[u].ans = (a[s] == (1 << k) - 1 ? 1 : MAXN);
      v[u].len = 1;
      if(a[s]) {
        v[u].pre.emplace_back(a[s], 1);
        v[u].suf.emplace_back(a[s], 1);
      }
      return;
    }
    int mid = (s + t) >> 1;
    build(u << 1, s, mid), build((u << 1) | 1, mid + 1, t);
    v[u] = v[u << 1] + v[(u << 1) | 1];
  }
  void update(int u, int p, int x) {
    if(l[u] == r[u]) {
      v[u].ans = (x == (1 << k) - 1 ? 1 : MAXN);
      v[u].pre.clear(), v[u].suf.clear();
      if(x) {
        v[u].pre.emplace_back(x, 1);
        v[u].suf.emplace_back(x, 1);
      }
      return;
    }
    (p <= r[u << 1] ? update(u << 1, p, x) : update((u << 1) | 1, p, x));
    v[u] = v[u << 1] + v[(u << 1) | 1];
  }
  INFO GetINFO(int u, int s, int t) {
    if(l[u] >= s && r[u] <= t) {
      return v[u];
    }
    if(s <= r[u << 1] && t >= l[(u << 1) | 1]) {
      return GetINFO(u << 1, s, t) + GetINFO((u << 1) | 1, s, t);
    }else if(t <= r[u << 1]) {
      return GetINFO(u << 1, s, t);
    }else {
      return GetINFO((u << 1) | 1, s, t);
    }
  }
}tr;

int n, q;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> k;
  for(int i = 1; i <= n; ++i) {
    cin >> tr.a[i];
  }
  tr.build(1, 1, n);
  cin >> q;
  for(int i = 1, op, p, x, l, r; i <= q; ++i) {
    cin >> op;
    if(op == 1) {
      cin >> p >> x;
      tr.update(1, p, x);
    }else {
      cin >> l >> r;
      int ans = tr.GetINFO(1, l, r).ans;
      cout << (ans == MAXN ? -1 : ans) << "\n";
    }
  }
  return 0;
}
posted @ 2024-09-25 21:18  Yaosicheng124  阅读(3)  评论(0编辑  收藏  举报