数据结构入门级题目瞎做
\(1.\) [省选联考 2021 A/B 卷] 宝石
观察到题目中收集的顺序都已经给定,所以对于每一个宝石,收集完它之后的下一个宝石的位置是知道的。于是,我们可以倍增预处理出每个位置的宝石被收集后第 \(2^x\) 个该收集的宝石的位置。把路径拆成 \(s - lca\) 和 \(lca - t\)。\(s - lca\) 部分可以直接倍增。但是 \(lca - t\) 该怎么办呢?逆着题目中给的宝石顺序再倍增预处理一下,然后从终点跳。由于我们不知道最终的位置是啥,并且这个位置具有单调性,所以我们可以二分这个终点 \(mid\)。然后的问题就是起点和终点不一定有第一个和第 \(mid\) 个宝石。于是我们考虑把询问离线下来, \(dfs\) 一遍,对于每一种宝石开一个 \(vector\) 即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
const int N = 2e5 + 10, M = 5e4 + 10;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int n, m, c, tot, u, v, t, q;
int p[M], a[N], head[N], ver[N * 2], last[N * 2], f[N][30], L[N], lt[N][30], nt[N][30];
int nf[N], dep[N], b[M], d[M], back[M], ans[N];
vector<int> ct[M];
vector<pii> qq[N];
void add(int x, int y)
{
ver[++tot] = y;
last[tot] = head[x];
head[x] = tot;
}
void dfs(int x, int fa)
{
ct[a[x]].push_back(x);
dep[x] = dep[fa] + 1;
if (!ct[p[1]].empty()) nf[x] = ct[p[1]].back();
for (int i = head[x]; i; i = last[i])
{
int y = ver[i];
if (y == fa) continue;
f[y][0] = x;
for (int j = 1; j <= t; j++) f[y][j] = f[f[y][j - 1]][j - 1];
if (!ct[b[a[y]]].empty()) nt[y][0] = ct[b[a[y]]].back();
if (!ct[d[a[y]]].empty()) lt[y][0] = ct[d[a[y]]].back();
for (int j = 1; j <= t; j++) nt[y][j] = nt[nt[y][j - 1]][j - 1];
for (int j = 1; j <= t; j++) lt[y][j] = lt[lt[y][j - 1]][j - 1];
dfs(y, x);
}
ct[a[x]].pop_back();
}
int lca(int x, int y)
{
if (dep[x] < dep[y]) swap(x, y);
for (int i = t; i >= 0; i--) {if (dep[f[x][i]] >= dep[y]) x = f[x][i];}
if (x == y) return x;
for (int i = t; i >= 0; i--)
{
if (f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
int find(int x, int y, int flag)
{
if (!x) return -1;
if (dep[x] < dep[y]) return -1;
if (flag) {for (int i = t; i >= 0; i--) {if (dep[nt[x][i]] >= dep[y]) x = nt[x][i];} return x;}
for (int i = t; i >= 0; i--) {if (dep[lt[x][i]] >= dep[y]) x = lt[x][i];} return x;
}
void dfs2(int x, int fa)
{
ct[a[x]].push_back(x);
int len = qq[x].size();
for (int i = 0; i < len; i++)
{
int s = qq[x][i].first, LCA = lca(x, s);
int pos = find(nf[s], LCA, 1);
int l = back[a[pos]] + 1, r = c, mid;
ans[qq[x][i].second] = back[a[pos]];
while (l <= r)
{
mid = (l + r) >> 1;
if (!ct[p[mid]].empty())
{
int pp = find(ct[p[mid]].back(), LCA, 0);
if (pp == -1) r = mid - 1;
else
{
if (back[a[pp]] <= back[a[pos]] + 1) l = mid + 1, ans[qq[x][i].second] = mid;
else r = mid - 1;
}
}
else r = mid - 1;
}
}
for (int i = head[x]; i; i = last[i])
{
int y = ver[i];
if (y == fa) continue;
dfs2(y, x);
}
ct[a[x]].pop_back();
}
int main()
{
for (int i = 2; i <= 2e5; i++) L[i] = L[i >> 1] + 1;
n = read(), m = read(), c = read(); t = L[n];
for (int i = 1; i <= c; i++) p[i] = read(), b[p[i - 1]] = p[i], d[p[i]] = p[i - 1], back[p[i]] = i;
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i < n; i++)
{
u = read(), v = read();
add(u, v); add(v, u);
}
q = read();
for (int i = 1; i <= q; i++)
{
u = read(), v = read();
qq[v].push_back(mp(u, i));
}
dfs(1, 0);
dfs2(1, 0);
for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
return 0;
}
\(3.\) [AHOI2013] 作业 代码
\(4.\) [SNOI2017] 一个简单的询问 代码
\(5.\) [Ynoi2016] 这是我自己的发明 代码
\(6.\) [ZJOI2013] K大数查询 代码
\(7.\) [国家集训队] 矩阵乘法
\(8.\) [模板] 二逼平衡树
\(9.\) [NOI2010] 超级钢琴
\(10.\) [HEOI2016/TJOI2016]排序
\(11.\) CF444C DZY Loves Colors
\(12.\) CF1638C Colorful Operations