专栏文章
题解:P11967 [GESP202503 八级] 割裂
P11967题解参与者 8已保存评论 7
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 7 条
- 当前快照
- 1 份
- 快照标识符
- @mipqo8mm
- 此快照首次捕获于
- 2025/12/03 16:22 3 个月前
- 此快照最后确认于
- 2025/12/03 16:22 3 个月前

我们来看这棵树,假设好点对是 ,那么删除其中 都不符合题意,观察这个结果,发现这个其实就是这棵树上的路径。
但是,这题最多有 个点对,一个一个枚举肯定是不行的,其实这里只需要统计每一个点有没有被一个好点对的路径经过,考虑用树上差分,假设我们要加入好点对 ,那么我们在点 标记加 ,然后此时 实际被统计了两次,所以标记 减 ,然后 上面是没有被经过的,所以令 的父亲节点减 。
此时标记如下:

标记完了后我们就统计每个子树内的节点和,最终就变成这个样子:

这样,每个点对只作 次标记,总时间复杂度 。
CPP#include<iostream>
#include<vector>
using namespace std;
const int N = 1e6 + 10;
int n, q, u, v, a[N], fa[N][20], dep[N], cnt;
vector<int> g[N];
void dfs(int u, int f){
fa[u][0] = f;
dep[u] = dep[f] + 1;
for(int i = 1; i < 20; i++) fa[u][i] = fa[fa[u][i-1]][i-1];
for(int v: g[u]){
if(v == f) continue;
dfs(v, u);
}
}
int lca(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
for(int i = 19; i >= 0; i--){
if((dep[u] - dep[v]) & (1 << i)) u = fa[u][i];
}
if(u == v) return u;
for(int i = 19; i >= 0; i--){
if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
}
return fa[u][0];
}
void get_ans(int u, int f){
for(int v: g[u]){
if(v == f) continue;
get_ans(v, u);
a[u] += a[v];
}
}
int main(){
cin >> n >> q;
for(int i = 1; i < n; i++){
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
while(q--){
cin >> u >> v;
int w = lca(u, v);
a[u]++; a[v]++;
a[w]--; a[fa[w][0]]--;
}
get_ans(1, 0);
cin >> u >> v;
int w = lca(u, v);
while(u != w){
if(a[u] == 0) cnt++;
u = fa[u][0];
}
while(v != w){
if(a[v] == 0) cnt++;
v = fa[v][0];
}
if(a[u] == 0) cnt++;
cout << cnt;
return 0;
}
相关推荐
评论
共 7 条评论,欢迎与作者交流。
正在加载评论...