【洛谷P2147】洞穴勘测
题目大意:维护 N 个点的无向图,支持动态加边和删边,回答两点的连通性。
题解:线段树分治 + 可撤销并查集
询问可以离线,这是线段树分治的基础。
建立在操作时间轴上的线段树称为线段树分治算法。
本题中线段树维护的是当前时间段中出现的边的集合。分析可知,对于一条边来说,至多出现在线段树上 \(O(logm)\) 个节点的集合中,至多 \(M\) 条边,因此,线段树上的边集合大小一共为 \(O(mlogm)\)。建立好线段树之后,从根开始 dfs 整棵树,每经过一个节点时,将当前时间区间内出现的边加入并查集中,并递归到子树;离开当前节点之前,需要将当前节点边集中的边从并查集中删除。若到了时间区间的叶子节点,则回答对应时间的询问即可。带撤销的并查集采用按秩合并,单次操作的时间复杂度为 \(O(logn)\)。故总的时间复杂度为 \(O(mlogmlogn)\)。
代码如下
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
const int maxm = 2e5 + 10;
typedef pair<int, int> PI;
int n, m;
map<PI, int> mp;
map<int, PI> qry;
vector<int> ans;
struct node {
int x, y, rx, ry;
};
stack<node> stk;
int fa[maxn], rk[maxn];
int find(int x) {
while (x != fa[x]) {
x = fa[x];
}
return x;
}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return;
}
if (rk[x] > rk[y]) {
swap(x, y);
}
stk.push(node {x, y, rk[x], rk[y]});
fa[x] = y;
if (rk[x] == rk[y]) {
++rk[y];
}
}
void undo() {
node t = stk.top();
fa[t.x] = t.x, fa[t.y] = t.y;
rk[t.x] = t.rx, rk[t.y] = t.ry;
stk.pop();
}
int ask(int x, int y) {
return find(x) == find(y) ? 1 : 2;
}
struct SegTree {
#define ls(o) t[o].lc
#define rs(o) t[o].rc
int lc, rc;
vector<PI> s;
} t[maxm << 1];
int rt;
void build(int &o, int l, int r) {
static int tot = 0;
o = ++tot;
if (l == r) {
return;
}
int mid = l + r >> 1;
build(ls(o), l, mid);
build(rs(o), mid + 1, r);
}
void insert(int o, int l, int r, int x, int y, PI z) {
if (l == x && r == y) {
t[o].s.push_back(z);
return;
}
int mid = l + r >> 1;
if (y <= mid) {
insert(ls(o), l, mid, x, y, z);
} else if (x > mid) {
insert(rs(o), mid + 1, r, x, y, z);
} else {
insert(ls(o), l, mid, x, mid, z);
insert(rs(o), mid + 1, r, mid + 1, y, z);
}
}
void dfs(int o, int l, int r) {
for (auto e : t[o].s) {
merge(e.first, e.second);
}
if (l == r) {
if (qry.find(l) != qry.end()) {
PI q = qry[l];
ans.push_back(ask(q.first, q.second));
}
for (auto e : t[o].s) {
undo();
}
return;
}
int mid = l + r >> 1;
dfs(ls(o), l, mid);
dfs(rs(o), mid + 1, r);
for (auto e : t[o].s) {
undo();
}
}
int main() {
static char s[10];
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
fa[i] = i, rk[i] = 1;
}
build(rt, 1, m);
for (int i = 1, x, y; i <= m; i++) {
scanf("%s %d %d", s, &x, &y);
if (x > y) {
swap(x, y);
}
if (s[0] == 'C') {
mp[PI(x, y)] = i;
} else if (s[0] == 'D') {
auto p = mp.find(PI(x, y));
assert(p != mp.end());
insert(rt, 1, m, p->second, i, p->first);
mp.erase(p);
} else {
qry[i] = PI(x, y);
}
}
for (auto e : mp) {
insert(rt, 1, m, e.second, m, e.first);
}
dfs(rt, 1, m);
for (int i = 0; i < ans.size(); i++) {
puts(ans[i] == 1 ? "Yes" : "No");
}
return 0;
}
【洛谷P2147】洞穴勘测的更多相关文章
- 洛谷P2147[SDOI2008]洞穴勘测(lct)
题目描述 辉辉热衷于洞穴勘测. 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假 ...
- 洛谷P2147 [SDOI2008]Cave 洞穴勘测
题目描述 辉辉热衷于洞穴勘测. 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假 ...
- 洛谷P2147 [SDOI2008] 洞穴勘探 [LCT]
题目传送门 洞穴勘探 题目描述 辉辉热衷于洞穴勘测. 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道 ...
- [洛谷P2147][SDOI2008]洞穴勘测
题目大意:有$n$个洞穴,$m$条指令,指令有三种 $Connect\;u\;v$:在$u,v$之间连一条边 $Destroy\;u\;v$:切断$u,v$之间的边 $Query\;u\;v$:询问$ ...
- 【洛谷 P2147】 [SDOI2008]洞穴勘测(LCT)
题目链接 LCT裸题.. #include <cstdio> #define R register int #define I inline void #define lc c[x][0] ...
- 洛谷 P2147 [SDOI2008]洞穴勘测 (线段树分治)
题目链接 题解 早就想写线段树分治的题了. 对于每条边,它存在于一段时间 我们按时间来搞 我们可把一条边看做一条线段 我们可以模拟线段树操作,不断分治下去 把覆盖\(l-r\)这段时间的线段筛选出来, ...
- 洛谷 P2147 [SDOI2008]洞穴勘测
以下这个做法应该是叫线段树分治... 根据修改操作预处理出每条边存在的时间区间[l,r](以操作序号为时间),然后把所有形式化后的修改挂到线段树节点上. 处理完修改后,dfs一遍线段树,进入某个节点时 ...
- 题解 洛谷P2147/BZOJ2049【[SDOI2008]洞穴勘测】
Link-Cut-Tree的模板题啊......(听说还可以用其他的方法做,不管了,直接上LCT) 没有要求维护点权,只需要维护点的连通性即可. 就是朴素的LCT,居然还不要pushup. 感觉有些不 ...
- 洛谷 P2147 [SDOI2008]洞穴勘测 LCT
Code: #include <cstdio> #include <algorithm> #include <string> #include <cstrin ...
随机推荐
- java-字符
注意第16行处理方式.
- 【机器学习】Jackknife,Bootstraping, bagging, boosting, AdaBoosting, Rand forest 和 gradient boosting
Jackknife,Bootstraping, bagging, boosting, AdaBoosting, Rand forest 和 gradient boosting 这些术语,我经常搞混淆, ...
- IIS调试
点击调试程序的时候默认启动TFS内置的调试服务器http://localhost:51607/,在该处填写自己在IIS上配置的站点就能进入自己的站点调试http://mr.ciwong.com
- EL表达式的11隐含对象
EL表达式在不同范围如何取值: <% pageContext.setAttribute("book", "红楼梦"); request.setAttrib ...
- ADG环境搭建
一:实验环境介绍PC机系统: CentOS 6.5(64位)数据库版本: Oracle 11gR2 11.2.0.4 (64位)IP地址规划:主数据库10.110.9.41 SID:orapridb_ ...
- .Net Core控制台应用加载读取Json配置文件
⒈添加依赖 Microsoft.Extensions.Configuration Microsoft.Extensions.Configuration.FileExtensions Microsoft ...
- 为什么说Python采用的是基于值的内存管理模式?
Python中的变量并不直接存储值,而是存储了值的内存地址或者引用,假如为不同变量赋值为相同值,这个值在内存中只有一份,多个变量指向同一块内存地址.
- O009、KVM 网络虚拟化基础
参考https://www.cnblogs.com/CloudMan6/p/5289590.html 网络虚拟化是虚拟化技术中最复杂的部分,学习难度最大. 但因为网络是虚拟化中非常重要的资源, ...
- 详解EveryThing
摘要:Everything几乎是每个职场人必备的效率工具,但同事们都只用它的一两个基本功能,并没有发挥出该软件的真正效率.实际上,把Everything的功能用到极致能够成倍的提升我们的工作效率,本文 ...
- 把两个object对象合并成一个对象 属性名称相同的变为后面对象的值
object.assign(from,obj)------object.assign(目标对象,被合并的对象)