当前位置: 首页 > news >正文

洛谷 P11967 [GESP202503 八级] 割裂-普及+/提高

题目描述

小杨有一棵包含 $ n $ 个节点的树,其中节点的编号从 1 到 $ n $。

小杨设置了一个好点对 { ⟨ u 1 , v 1 ⟩ , ⟨ u 2 , v 2 ⟩ , … , ⟨ u a , v a ⟩ } \{\langle u_1, v_1 \rangle, \langle u_2, v_2 \rangle, \dots, \langle u_a, v_a \rangle\} {⟨u1,v1,u2,v2,,ua,va⟩} 和一个坏点对 ⟨ b u , b v ⟩ \langle b_u, b_v \rangle bu,bv。一个节点能被删除,当且仅当:

  • 删除该节点后对于所有的 $ 1 \leq i \leq a $,好点对 $ u_i $ 和 $ v_i $ 仍然连通;
  • 删除该节点后坏点对 $ b_u $ 和 $ b_v $ 不连通。

如果点对中的任意一个节点被删除,其视为不连通。

小杨想知道,还有多少个节点能被删除。

输入格式

第一行包含两个非负整数 $ n $, $ a $,含义如下题面所示。

接下来 n − 1 n - 1 n1 行,每行包含两个正整数 $ x_i, y_i $,代表存在一条连接节点 $ x_i $ 和 $ y_i $ 的边;

之后 $ a $ 行,每行包含两个正整数 $ u_i, v_i $,代表一个好点对 $ \langle u_i, v_i \rangle $;

最后一行包含两个正整数 $ b_u, b_v $,代表坏点对 $ \langle b_u, b_v \rangle $。

输出格式

输出一个非负整数,代表删除的节点个数。

输入输出样例 #1

输入 #1

6 2
1 3
1 5
3 6
3 2
5 4
5 4
5 3
2 6

输出 #1

2

说明/提示

子任务编号分值$ n $$ a $
1 20 20 20 = 10 =10 =10 = 0 =0 =0
2 20 20 20$ \leq 100 $$ \leq 100 $
3 60 60 60$ \leq 10^6 $$ \leq 10^5 $

对于全部数据,保证有 $ 1 \leq n \leq 10^6 $, $ 0 \leq a \leq 10^5 $, $ u_i \neq v_i $, $ b_u \neq b_v $。

solution

如果 u ,v 的路径上的一点被删除,则 u, v 不联通,所以被删除的点应该满足两个条件

  • 在坏点对点路径上
  • 不在任何好点对的路径上
    所以可以遍历坏点对路径上的每一点,如果它不在任何好点对上则满足条件。可以结合最近公共祖先完成判断。

代码

#include <iostream>
#include "bit"
#include "vector"
#include "unordered_set"
#include "unordered_map"
#include "set"
#include "queue"
#include "algorithm"
#include "bitset"
#include "cstring"
#include "cmath"using namespace std;const int N = 1e6 + 1, M = 1e5 + 1;
int n, a, U[M], V[M], s, t, f[N][21], d[N];
vector<int> e[N];void dfs(int u, int p) {d[u] = d[p] + 1;f[u][0] = p;for (int i = 1; i <= 20; i++) f[u][i] = f[f[u][i - 1]][i - 1];for (int v: e[u]) {if (v != p) {dfs(v, u);}}
}int lca(int x, int y) {if (x == y) return x;if (d[x] < d[y]) swap(x, y);for (int i = 20; d[x] > d[y]; i--) {if (d[f[x][i]] >= d[y])x = f[x][i];}if (x == y) return x;for (int i = 20; i >= 0; i--) {if (f[x][i] != f[y][i])x = f[x][i], y = f[y][i];}return f[x][0];
}int main() {scanf("%d %d", &n, &a);for (int i = 1; i < n; i++) {int x, y;scanf("%d %d", &x, &y);e[x].push_back(y);e[y].push_back(x);}for (int i = 1; i <= a; i++) scanf("%d %d", &U[i], &V[i]);scanf("%d %d", &s, &t);dfs(1, 0);int p = lca(s, t);int cnt = 0;for (; s != p; s = f[s][0]) {int tt = 1;for(int i = 1; i <= a; i++){int P =  lca(U[i], V[i]);if(lca(P, s) == P && (lca(s, U[i]) == s || lca(s, V[i]) == s)){tt = 0;break;}}cnt += tt;}for (; ; t = f[t][0]) {int tt = 1;for(int i = 1; i <= a; i++){int P =  lca(U[i], V[i]);if(lca(P, t) == P && (lca(t, U[i]) == t || lca(t, V[i]) == t)){tt = 0;break;}}cnt += tt;if(t == p) break;}cout << cnt;
}
  • 用 dfn 序求最近公共祖先,可以将对数级别降低到常数级别。
#include <iostream>
#include "bit"
#include "vector"
#include "unordered_set"
#include "unordered_map"
#include "set"
#include "queue"
#include "algorithm"
#include "bitset"
#include "cstring"
#include "cmath"using namespace std;const int N = 1e6 + 1, M = 1e5 + 1;
int n, a, U[M], V[M], s, t, f[N][21], id, dfn[N];
vector<int> e[N];void dfs(int u, int p) {f[dfn[u] = ++id][0] = p;for (int v: e[u]) {if (v != p) {dfs(v, u);}}
}inline int my_min(int x, int y){return dfn[x] < dfn[y] ? x : y;
}int lca(int x, int y) {if (x == y) return x;x = dfn[x], y = dfn[y];if (x > y) swap(x, y);int d = (int)log2(y - x);x++;// 求 x y 之间 dfn 最小的那个数return my_min(f[x][d], f[y - (1 << d) + 1][d]);
}int main() {scanf("%d %d", &n, &a);for (int i = 1; i < n; i++) {int x, y;scanf("%d %d", &x, &y);e[x].push_back(y);e[y].push_back(x);}for (int i = 1; i <= a; i++) scanf("%d %d", &U[i], &V[i]);scanf("%d %d", &s, &t);dfs(1, 0);for (int j = 1; j < log2(n) + 1; j++) {for (int i = 1; i + (1 << (j - 1)) <= n; i++) {f[i][j] = my_min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);}}int p = lca(s, t);int cnt = 0;for (; s != p; s = f[dfn[s]][0]) {int tt = 1;for (int i = 1; i <= a; i++) {int P = lca(U[i], V[i]);if (lca(P, s) == P && (lca(s, U[i]) == s || lca(s, V[i]) == s)) {tt = 0;break;}}cnt += tt;}for (;; t = f[dfn[t]][0]) {int tt = 1;for (int i = 1; i <= a; i++) {int P = lca(U[i], V[i]);if (lca(P, t) == P && (lca(t, U[i]) == t || lca(t, V[i]) == t)) {tt = 0;break;}}cnt += tt;if (t == p) break;}cout << cnt;}
  • 由于输入数据量达到百万级别,改用 getchar 快速读入可以节约时间
#include <iostream>
#include "bit"
#include "vector"
#include "unordered_set"
#include "unordered_map"
#include "set"
#include "queue"
#include "algorithm"
#include "bitset"
#include "cstring"
#include "cmath"using namespace std;const int N = 1e6 + 1, M = 1e5 + 1;
int n, a, U[M], V[M], s, t, f[N][21], id, dfn[N];
vector<int> e[N];void dfs(int u, int p) {f[dfn[u] = ++id][0] = p;for (int v: e[u]) {if (v != p) {dfs(v, u);}}
}inline int read() {int s = 0, w = 1;int ch = getchar();while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = getchar(); }while (ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar();}return s * w;
}inline int my_min(int x, int y){return dfn[x] < dfn[y] ? x : y;
}int lca(int x, int y) {if (x == y) return x;x = dfn[x], y = dfn[y];if (x > y) swap(x, y);int d = (int)log2(y - x);x++;// 求 x y 之间 dfn 最小的那个数return my_min(f[x][d], f[y - (1 << d) + 1][d]);
}int main() {n = read(), a = read();for (int i = 1; i < n; i++) {int x, y;x = read(), y = read();e[x].push_back(y);e[y].push_back(x);}for (int i = 1; i <= a; i++) U[i] = read(), V[i] = read();//scanf("%d %d", &U[i], &V[i]);s = read(), t = read();dfs(1, 0);for (int j = 1; j < log2(n) + 1; j++) {for (int i = 1; i + (1 << (j - 1)) <= n; i++) {f[i][j] = my_min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);}}int p = lca(s, t);int cnt = 0;for (; s != p; s = f[dfn[s]][0]) {int tt = 1;for (int i = 1; i <= a; i++) {int P = lca(U[i], V[i]);if (lca(P, s) == P && (lca(s, U[i]) == s || lca(s, V[i]) == s)) {tt = 0;break;}}cnt += tt;}for (;; t = f[dfn[t]][0]) {int tt = 1;for (int i = 1; i <= a; i++) {int P = lca(U[i], V[i]);if (lca(P, t) == P && (lca(t, U[i]) == t || lca(t, V[i]) == t)) {tt = 0;break;}}cnt += tt;if (t == p) break;}cout << cnt;
}

结果

用树上倍增求最近公共祖先的时间
在这里插入图片描述
用 dfn 序列求最近公共祖先的时间
在这里插入图片描述
改用快速读入时通过的时间
在这里插入图片描述

小结

这高低复杂度用树上差分好像复杂度更优一点

http://www.dtcms.com/a/267662.html

相关文章:

  • 百度文心 4.5 大模型详解:ERNIE 4.5 Technical Report
  • 水下航行器外形之变体式与回转体的区别
  • 线程锁和线程同步
  • 从“电话催维修“到“手机看进度“——售后服务系统开发如何重构客户体验
  • Linux网络配置与故障排除完全指南
  • 12 nacos配置中心
  • 使用Kahn算法处理节点依赖关系
  • ABB焊接机器人智能节气仪
  • 汽车制造车间检测机器人与PLC无线以太网实时控制方案
  • 数据库学习笔记(十七)--触发器的使用
  • Java SE--数组
  • 前端相关性能优化笔记
  • TEXT Complete Search
  • 【RK3568 编译rtl8723DU驱动】
  • Write-up:hacker_dns
  • 安达发|告别低效排产:APS高级排程如何助力电池企业智造升级?
  • Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备能耗实时监测与节能优化中的应用(332)
  • 09_云原生架构:拥抱不确定性
  • 【力扣 简单 C】746. 使用最小花费爬楼梯
  • AI小智项目全解析:软硬件架构与开发环境配置
  • 自动化Prompt生成平台的研发体系设计
  • [HDLBits] Cs450/history shift
  • vue router 里push方法重写为什么要重绑定this
  • Xmind功能特点
  • LucidShape 2024.09 最新
  • 2025年3月青少年电子学会等级考试 中小学生python编程等级考试三级真题答案解析(判断题)
  • Docker文件操作、数据卷、挂载
  • Servlet学习
  • FFmpeg——基础知识及FFmpeg框架
  • MySQL GROUP_CONCAT函数实现列转行