题解:P8339 [AHOI2022] 钥匙
题意:很简单了,不再赘述。
做法:
首先我们注意到每种颜色之间的贡献是相互独立的,我们可以分别拉出来虚树处理。
因为每种钥匙只有 \(5\) 把,那么我们考虑一个钥匙怎么做贡献,那么肯定是从某个点到这个钥匙再向别的方向走到一个宝箱,这个事情其实类似于括号序列。
所以我们可以从每个钥匙开始搜索虚树,类似括号序列的方法去找到配对的宝箱并记录这些贡献对,对于询问,那么就是要看这条路径 \((u,v)\) 覆盖的贡献对的价值之和。
这个是经典的二维数点问题,这里讲细一点。
我们考虑讨论一下一个贡献对的形态:
-
\((u,v)\) 中其中一个是另一个的祖先,这里假设 \(u\) 是 \(v\) 的祖先,那么就要求这个路径的一个起点在 \(u\) 除了 \(v\) 这一侧的子树外,且终点在 \(v\) 内。假设 \(v\) 一直向上跳直到父亲是 \(u\) 跳到的节点是 \(p\),那么我们把树拍到 dfs 序上,就要求询问起点在 \([1, dfn_p - 1]\bigcup[out_p, n]\),终点在 \([dfn_v, out_v]\) 中。其中 \(dfn\) 是 dfs 序的编号,\(out\) 是节点 \(u\) 子树中 dfs 序最大的编号。
-
没有祖先关系,那么要求起点在 \([dfn_u, out_u]\),终点在 \([dfn_v,out_v]\) 中。
直接二维数点即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2.5e6 + 5;
int n, m, t[maxn / 5], c[maxn / 5], dfn[maxn / 5], out[maxn / 5], st[maxn / 5 * 2][21], lg[maxn / 5 * 2], cnt, dep[maxn / 5];
vector<int> v[maxn / 5], e[maxn / 5], g[maxn / 5];
void dfs(int u, int fa) {
st[++cnt][0] = u; dfn[u] = cnt;
dep[u] = dep[fa] + 1;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
if(v == fa)
continue;
dfs(v, u);
st[++cnt][0] = u;
}
out[u] = cnt;
}
int getmin(int x, int y) {
return (dep[x] < dep[y] ? x : y);
}
void prepare() {
lg[0] = -1;
for (int i = 1; i <= cnt; i++)
lg[i] = lg[i >> 1] + 1;
for (int j = 1; (1 << j) <= cnt; j++)
for (int i = 1; i + (1 << j) - 1 <= cnt; i++)
st[i][j] = getmin(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
}
int lca(int x, int y) {
x = dfn[x], y = dfn[y];
if(x > y)
swap(x, y);
int k = lg[y - x + 1];
return getmin(st[x][k], st[y - (1 << k) + 1][k]);
}
bool cmp(int x, int y) {
return dfn[x] < dfn[y] ? 1 : 0;
}
struct node {
int st, ed;
} x[maxn];
int tot, nw;
void dfs_c(int u, int col, int cnt, int fa, int rt) {
// cout << u << " " << cnt << endl;
if(c[u] == col)
(t[u] == 1 ? cnt++ : cnt--);
if(!cnt) {
x[++tot] = {rt, u};
return ;
}
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if(v == fa)
continue;
dfs_c(v, col, cnt, u, rt);
}
}
void build(int col) {
if(!v[col].size())
return ;
sort(v[col].begin(), v[col].end(), cmp);
int len = v[col].size();
for (int i = 0; i < len - 1; i++)
v[col].push_back(lca(v[col][i], v[col][i + 1]));
sort(v[col].begin(), v[col].end(), cmp);
len = unique(v[col].begin(), v[col].end()) - v[col].begin();
// cout << col << " asd" << len << endl;
while(v[col].size() > len)
v[col].pop_back();
for (int i = 0; i < v[col].size() - 1; i++) {
int x = v[col][i], y = v[col][i + 1];
int d = lca(x, y);
g[d].push_back(y), g[y].push_back(d);
}
for (int i = 0; i < len; i++)
if(col == c[v[col][i]] && t[v[col][i]] == 1) {
// cout << v[col][i] << endl;
dfs_c(v[col][i], col, 0, 0, v[col][i]);
}
for (int i = 0; i < v[col].size(); i++)
g[v[col][i]].clear();
}
#define lowbit(x) (x & (-x))
struct Tree_array {
int tr[maxn], n;
void add(int x, int val) {
while(x <= n)
tr[x] += val, x += lowbit(x);
}
int query(int x) {
int ans = 0;
while(x)
ans += tr[x], x -= lowbit(x);
return ans;
}
} tree;
struct node1 {
int sy, ey, h, id;
friend bool operator<(node1 x, node1 y) {
return x.h < y.h;
}
} y[maxn * 4];
int totn;
void prepare_mat() {
for (int i = 1; i <= tot; i++) {
int u = x[i].st, v = x[i].ed;
int d = lca(u, v);
// cout << u << " " << v << " asdf" << d << endl;
if(u == d) {
for (int j = 0; j < e[u].size(); j++) {
int p = e[u][j];
if(dfn[p] <= dfn[v] && out[p] >= dfn[v] && dep[p] > dep[u]) {
u = p;
y[++totn] = {1, dfn[u] - 1, dfn[v], 1};
y[++totn] = {out[u] + 1, cnt, dfn[v], 1};
y[++totn] = {1, dfn[u] - 1, out[v] + 1, -1};
y[++totn] = {out[u] + 1, cnt, out[v] + 1, -1};
// cout << dfn[u] << " " << dfn[v] << endl;
break;
}
}
}
else if(v == d) {
for (int j = 0; j < e[v].size(); j++) {
int p = e[v][j];
if(dfn[p] <= dfn[u] && out[p] >= dfn[u] && dep[p] > dep[v]) {
v = p;
y[++totn] = {dfn[u], out[u], 1, 1};
y[++totn] = {dfn[u], out[u], dfn[v], -1};
y[++totn] = {dfn[u], out[u], out[v] + 1, 1} ;
y[++totn] = {dfn[u], out[u], cnt + 1, -1};
break;
}
}
}
else {
y[++totn] = {dfn[u], out[u], dfn[v], 1};
y[++totn] = {dfn[u], out[u], out[v] + 1, -1};
}
}
y[++totn] = {1, cnt, cnt + 1, 0};
sort(y + 1, y + totn + 1);
}
struct query {
int x, y, id;
friend bool operator<(query x, query y) {
return x.y < y.y;
}
} q[maxn];
int totq, ans[maxn];
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> t[i] >> c[i], v[c[i]].push_back(i);
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1, 0);
prepare();
for (int i = 1; i <= n; i++)
build(i);
prepare_mat();
for (int i = 1; i <= m; i++) {
int x, y; cin >> x >> y;
x = dfn[x], y = dfn[y];
q[++totq] = {x, y, i};
}
sort(q + 1, q + m + 1);
tree.n = cnt;
int pos = 1;
for (int i = 1; i <= totn; i++) {
while(pos <= m && q[pos].y < y[i].h) {
// cout << pos << " " << " " << q[pos].id << endl;
ans[q[pos].id] = tree.query(q[pos].x);
pos++;
}
tree.add(y[i].sy, y[i].id);
tree.add(y[i].ey + 1, -y[i].id);
// cout << y[i].sy << " " << y[i].ey << " " << y[i].h << " " << y[i].id << endl;
}
for (int i = 1; i <= m; i++)
cout << ans[i] << endl;
return 0;
}

浙公网安备 33010602011771号