2024初秋集训——提高组 #36

C. 经典字符串问题

题目描述

给定一个排列,对于每个数,我们都可以把它看作一个字符串。请求出 \([l,r]\) 中字典序第 \(k\) 小的数是什么。

思路

我们可以建一个可持久化字典树,上面记录每个前缀的每个数。这里我们要在这些数的后面插入 \(-1\),使得其长度相等,并方便我们判断字典序。

时空复杂度均为 \(O((N+Q)\log A_i)\)

代码

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

const int MAXN = 100005;

int Pow10[6];

struct Persistent_Trie {
  int tot = 1, ROOT[MAXN], son[11][10 * MAXN], cnt[10 * MAXN];
  void Insert(int x, int y, int val) {
    ROOT[x] = ++tot;
    int u = tot, v = ROOT[y], res = 0;
    bool op = 0;
    for(int i = 5; i >= 0; --i) {
      int num = val / Pow10[i] % 10 + 1;
      if(num > 1 || op) {
        cnt[u] = cnt[v] + 1;
        op = 1;
        for(int j = 0; j <= 10; ++j) {
          son[j][u] = son[j][v];
        }
        u = son[num][u] = ++tot, v = son[num][v];
      }else {
        res++;
      }
    }
    for(int i = 1; i <= res; ++i) {
      cnt[u] = cnt[v] + 1;
      for(int j = 0; j <= 10; ++j) {
        son[j][u] = son[j][v];
      }
      u = son[0][u] = ++tot, v = son[0][v];
    }
    cnt[u] = cnt[v] + 1;
  }
  int Find(int l, int r, int k) {
    if(k > r - l + 1) {
      return -1;
    }
    int u = ROOT[l - 1], v = ROOT[r], ret = 0;
    for(int i = 5; i >= 0; --i) {
      int res = 0;
      for(int j = 0; j <= 10; ++j) {
        if(res + cnt[son[j][v]] - cnt[son[j][u]] >= k) {
          k -= res;
          if(!j) {
            return ret;
          }
          ret = 10 * ret + j - 1;
          u = son[j][u], v = son[j][v];
          break;
        }
        res += cnt[son[j][v]] - cnt[son[j][u]];
      }
    }
    return ret;
  }
}tr;

int n, q;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> q;
  Pow10[0] = 1;
  for(int i = 1; i <= 5; ++i) {
    Pow10[i] = Pow10[i - 1] * 10;
  }
  for(int i = 1, x; i <= n; ++i) {
    cin >> x;
    tr.Insert(i, i - 1, x);
  }
  for(int i = 1, l, r, k; i <= q; ++i) {
    cin >> l >> r >> k;
    cout << tr.Find(l, r, k) << "\n";
  }
  return 0;
}

D. 圆与圆之间的距离是不能一概而论的

题目描述

给定 \(N\) 个圆,这些圆之间的关系只有分离,包含两种(不会相切,相交)。我们定义两个圆的距离为其间一条路径(可以弯曲)穿过圆弧数量的最小值(不包含端点)。有 \(Q\) 个查询,每次查询两个圆之间的距离。

思路

我们可以先对这些圆建树,一个圆的父亲为包含它的圆中半径最小的那个。求其在树上的距离即可。而难点就在于建树。

我们使用扫描线求解。我们可以将一个圆拆分成左右两半,每次找父亲时用 set 找到其左右第一个圆弧。如果左弧左边还是左弧,那么父亲就是此弧对应的圆。若是右弧,那么父亲为此弧的父亲。

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

代码

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

const int MAXN = 300005;

struct Node {
  int x, y, r, id;
}s[MAXN];

int posx;

ld inter(int i, int x) {
  int id = abs(i);
  return s[id].y + (i < 0 ? -1.0l : 1.0l) * sqrt((ld)(1ll * s[id].r * s[id].r - 1ll * (x - s[id].x) * (x - s[id].x)));
}

struct cmp {
  bool operator()(int a, int b) const {
    return inter(a, posx) < inter(b, posx) || (inter(a, posx) == inter(b, posx) && a < b);
  }
};

int n, q, f[18][MAXN], pos[MAXN], dep[MAXN];
vector<int> e[MAXN], X, vec[2 * MAXN];
vector<pii> ve[2 * MAXN];
set<int, cmp> S;

int LCA(int u, int v) {
  if(dep[u] < dep[v]) {
    swap(u, v);
  }
  int d = dep[u] - dep[v];
  for(int i = 17; i >= 0; --i) {
    if((d >> i) & 1) {
      u = f[i][u];
    }
  }
  if(u == v) {
    return u;
  }
  for(int i = 17; i >= 0; --i) {
    if(f[i][u] != f[i][v]) {
      u = f[i][u], v = f[i][v];
    }
  }
  return f[0][u];
}

void dfs(int u) {
  dep[u] = dep[f[0][u]] + 1;
  for(int i = 1; i <= 17; ++i) {
    f[i][u] = f[i - 1][f[i - 1][u]];
  }
  for(int v : e[u]) {
    dfs(v);
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  freopen("circle.in", "r", stdin);
  freopen("circle.out", "w", stdout);
  cin >> n;
  for(int i = 1; i <= n; ++i) {
    cin >> s[i].x >> s[i].y >> s[i].r;
    X.emplace_back(s[i].x - s[i].r);
    X.emplace_back(s[i].x + s[i].r);
  }
  sort(X.begin(), X.end()), X.erase(unique(X.begin(), X.end()), X.end());
  for(int i = 1; i <= n; ++i) {
    int l = lower_bound(X.begin(), X.end(), s[i].x - s[i].r) - X.begin() + 1;
    int r = lower_bound(X.begin(), X.end(), s[i].x + s[i].r) - X.begin() + 1;
    ve[l].emplace_back(i, 1);
    ve[r].emplace_back(i, -1);
    vec[l].emplace_back(i);
  }
  for(int i = 1; i <= X.size(); ++i) {
    posx = X[i - 1];
    for(auto [x, op] : ve[i]) {
      if(op == 1) {
        S.insert(x);
        S.insert(-x);
      }else {
        S.erase(x);
        S.erase(-x);
      }
    }
    for(int x : vec[i]) {
      auto it = S.find(-x), it2 = S.upper_bound(x);
      int id = 0;
      if(it != S.begin()) {
        it = prev(it);
        if(*it < 0) {
          id = -*it;
        }else {
          id = f[0][*it];
        }
      }
      if(it2 != S.end()) {
        if(*it2 > 0 && (!id || s[id].r > s[*it2].r)) {
          id = *it2;
        }
        if(*it2 < 0 && (!id || s[id].r > s[f[0][-*it2]].r)) {
          id = f[0][-*it2];
        }
      }
      f[0][x] = id;
      e[id].emplace_back(x);
    }
  }
  dfs(0);
  cin >> q;
  for(int i = 1, u, v; i <= q; ++i) {
    cin >> u >> v;
    int l = LCA(u, v);
    cout << dep[u] + dep[v] - 2 * dep[l] - (u != l) - (v != l) << "\n";
  }
  return 0;
}
posted @ 2024-10-17 20:04  Yaosicheng124  阅读(3)  评论(0编辑  收藏  举报