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;
}