题面

2049: [Sdoi2008]Cave 洞穴勘测

Time Limit: 10 Sec Memory Limit: 259 MB

Submit: 12030 Solved: 6024

Description

辉辉热衷于洞穴勘测。某天,他按照地图来到了一片被标记为JSZX的洞穴群地区。经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴。假如两个洞穴可以通过一条或者多条通道按一定顺序连接起来,那么这两个洞穴就是连通的,按顺序连接在一起的这些通道则被称之为这两个洞穴之间的一条路径。洞穴都十分坚固无法破坏,然而通道不太稳定,时常因为外界影响而发生改变,比如,根据有关仪器的监测结果,123号洞穴和127号洞穴之间有时会出现一条通道,有时这条通道又会因为某种稀奇古怪的原因被毁。辉辉有一台监测仪器可以实时将通道的每一次改变状况在辉辉手边的终端机上显示:如果监测到洞穴u和洞穴v之间出现了一条通道,终端机上会显示一条指令 Connect u v 如果监测到洞穴u和洞穴v之间的通道被毁,终端机上会显示一条指令 Destroy u v 经过长期的艰苦卓绝的手工推算,辉辉发现一个奇怪的现象:无论通道怎么改变,任意时刻任意两个洞穴之间至多只有一条路径。因而,辉辉坚信这是由于某种本质规律的支配导致的。因而,辉辉更加夜以继日地坚守在终端机之前,试图通过通道的改变情况来研究这条本质规律。然而,终于有一天,辉辉在堆积成山的演算纸中崩溃了……他把终端机往地面一砸(终端机也足够坚固无法破坏),转而求助于你,说道:“你老兄把这程序写写吧”。辉辉希望能随时通过终端机发出指令 Query u v,向监测仪询问此时洞穴u和洞穴v是否连通。现在你要为他编写程序回答每一次询问。已知在第一条指令显示之前,JSZX洞穴群中没有任何通道存在。

Input

第一行为两个正整数n和m,分别表示洞穴的个数和终端机上出现过的指令的个数。以下m行,依次表示终端机上出现的各条指令。每行开头是一个表示指令种类的字符串s("Connect”、”Destroy”或者”Query”,区分大小写),之后有两个整数u和v (1≤u, v≤n且u≠v) 分别表示两个洞穴的编号。

Output

对每个Query指令,输出洞穴u和洞穴v是否互相连通:是输出”Yes”,否则输出”No”。(不含双引号)

Sample Input

样例输入1 cave.in

200 5

Query 123 127

Connect 123 127

Query 123 127

Destroy 127 123

Query 123 127

样例输入2 cave.in

3 5

Connect 1 2

Connect 3 1

Query 2 3

Destroy 1 3

Query 2 3

Sample Output

样例输出1 cave.out

No

Yes

No

样例输出2 cave.out

Yes

No

HINT

数据说明 10%的数据满足n≤1000, m≤20000 20%的数据满足n≤2000, m≤40000 30%的数据满足n≤3000, m≤60000 40%的数据满足n≤4000, m≤80000 50%的数据满足n≤5000, m≤100000 60%的数据满足n≤6000, m≤120000 70%的数据满足n≤7000, m≤140000 80%的数据满足n≤8000, m≤160000 90%的数据满足n≤9000, m≤180000 100%的数据满足n≤10000, m≤200000 保证所有Destroy指令将摧毁的是一条存在的通道本题输入、输出规模比较大,建议c\c++选手使用scanf和printf进行I\O操作以免超时

分析

一条边的存在的时间其实就是一段连续的时间(我们这里指定每一行命令就是一个单位的时间),这样我们就可以把问题离线化。

那么我们可以用线段树来保存整个状态,这并不是什么难事,我们在线段树的每一个节点上保存一个边的集合,表示这个节点下所有的子节点都包含了这条边。

那么我们对于任意一个叶子节点,从根节点到叶子节点的全过程遍历到的所有的边组成的集合就是当前的图

由于如果每次询问都是从根节点出发的话效率太低,我们采用直接在线段树上移动的方式来解决效率问题。

通过可撤销并查集的性质,来实现在线段树上移动

AC code

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 10100;
  4. const int MAXM = 200100;
  5. typedef pair<int, int> pii;
  6. struct UFS {
  7. int f[MAXN];
  8. stack<pii> s;
  9. int finds(int x) {
  10. while (x ^ f[x])
  11. x = f[x];
  12. return x;
  13. }
  14. void unite(int x, int y) {
  15. x = finds(x);
  16. y = finds(y);
  17. if (x != y) {
  18. s.push({x, f[x]});
  19. f[x] = y;
  20. }
  21. }
  22. void init(int b, int e) { // 初始化函数,范围为 [b, e)
  23. for (int i = b; i < e; i++)
  24. f[i] = i;
  25. }
  26. void undo() {
  27. f[s.top().first] = s.top().second;
  28. s.pop();
  29. }
  30. };
  31. struct SegTree {
  32. vector<pii> data[MAXM << 2];
  33. static inline int lson(int k) { return k << 1; }
  34. static inline int rson(int k) { return (k << 1) | 1; }
  35. static inline int fat(int l, int r) { return (l + r) >> 1; }
  36. // add 函数对应于正常的线段树的 insert,但是稍微有些不同
  37. void add(int k, int l, int r, int x, int y, const pii &value) {
  38. if (l == x && r == y) {
  39. data[k].push_back(value);
  40. return;
  41. }
  42. int mid = fat(l, r);
  43. if (y <= mid) {
  44. add(lson(k), l, mid, x, y, value);
  45. } else if (x > mid) {
  46. add(rson(k), mid + 1, r, x, y, value);
  47. } else {
  48. add(lson(k), l, mid, x, mid, value);
  49. add(rson(k), mid + 1, r, mid + 1, y, value);
  50. }
  51. }
  52. };
  53. UFS ufs;
  54. SegTree segTree;
  55. vector<pair<pii, int> > que;
  56. int tar;
  57. // 通过 dfs 的方式在线段树上移动
  58. bool dfs(int k, int l, int r) {
  59. // 当完成一次询问之后,需要跳出当前的叶子,即回溯。通过 goto 来使得回溯的过程会自动进入正确的叶子节点
  60. rejudge:
  61. int target = que[tar].second;
  62. if (target == r && l == r) {
  63. if (ufs.finds(que[tar].first.first) == ufs.finds(que[tar].first.second))
  64. cout << "Yes" << endl;
  65. else
  66. cout << "No" << endl;
  67. tar++;
  68. return tar == que.size();// true 表示所有的询问已经结束,退出 dfs
  69. }
  70. int mid = SegTree::fat(l, r);
  71. if (target <= mid) {
  72. // for (auto &item: segTree.data[SegTree::lson(k)])
  73. // ufs.unite(item.first, item.second);
  74. for (int i = 0; i < segTree.data[SegTree::lson(k)].size(); ++i)
  75. ufs.unite(segTree.data[SegTree::lson(k)][i].first, segTree.data[SegTree::lson(k)][i].second);
  76. if (dfs(SegTree::lson(k), l, mid))
  77. return true;
  78. // for (auto &item: segTree.data[SegTree::lson(k)])
  79. for (int i = 0; i < segTree.data[SegTree::lson(k)].size(); ++i)
  80. ufs.undo();
  81. if (que[tar].second > r)
  82. return false;
  83. goto rejudge;
  84. } else {
  85. // for (auto &item: segTree.data[SegTree::rson(k)])
  86. // ufs.unite(item.first, item.second);
  87. for (int i = 0; i < segTree.data[SegTree::rson(k)].size(); ++i)
  88. ufs.unite(segTree.data[SegTree::rson(k)][i].first, segTree.data[SegTree::rson(k)][i].second);
  89. if (dfs(SegTree::rson(k), mid + 1, r))
  90. return true;
  91. // for (auto &item: segTree.data[SegTree::rson(k)])
  92. for (int i = 0; i < segTree.data[SegTree::rson(k)].size(); ++i)
  93. ufs.undo();
  94. if (que[tar].second > r)
  95. return false;
  96. goto rejudge;
  97. }
  98. }
  99. void solve() {
  100. int n, m;
  101. cin >> n >> m;
  102. map<pii, int> mp;
  103. for (int i = 0; i < m; ++i) {
  104. string s;
  105. int u, v;
  106. cin >> s >> u >> v;
  107. if (u > v) swap(u, v);
  108. switch (s[0]) {
  109. case 'Q':
  110. // que.push_back({{u, v}, i + 1});
  111. que.push_back(make_pair(make_pair(u, v), i + 1));
  112. break;
  113. case 'C':
  114. // mp.insert({{u, v}, i + 1});
  115. mp.insert(make_pair(make_pair(u, v), i + 1));
  116. break;
  117. case 'D': {
  118. // auto iter = mp.find({u, v});
  119. map<pii, int>::iterator iter = mp.find(make_pair(u, v));
  120. // segTree.add(1, 1, m, iter->second, i, {u, v});
  121. segTree.add(1, 1, m, iter->second, i, make_pair(u, v));
  122. mp.erase(iter);
  123. break;
  124. }
  125. }
  126. }
  127. map<pii, int>::iterator iter = mp.begin();
  128. while (iter != mp.end()) {
  129. segTree.add(1, 1, m, iter->second, m, iter->first);
  130. iter++;
  131. }
  132. ufs.init(0, n + 1);
  133. tar = 0;
  134. // for (auto &item: segTree.data[1])
  135. for (int i = 0; i < segTree.data[1].size(); ++i)
  136. ufs.unite(segTree.data[1][i].first, segTree.data[1][i].second);
  137. // ufs.unite(item.first, item.second);
  138. dfs(1, 1, m);
  139. return;
  140. }
  141. int main() {
  142. ios_base::sync_with_stdio(false);
  143. cin.tie(0);
  144. cout.tie(0);
  145. // cin.tie(nullptr);
  146. // cout.tie(nullptr);
  147. #ifdef ACM_LOCAL
  148. freopen("in.txt", "r", stdin);
  149. freopen("out.txt", "w", stdout);
  150. long long test_index_for_debug = 1;
  151. char acm_local_for_debug;
  152. while (cin >> acm_local_for_debug) {
  153. if (acm_local_for_debug == '$') exit(0);
  154. cin.putback(acm_local_for_debug);
  155. if (test_index_for_debug > 20) {
  156. throw runtime_error("Check the stdin!!!");
  157. }
  158. auto start_clock_for_debug = clock();
  159. solve();
  160. auto end_clock_for_debug = clock();
  161. cout << "Test " << test_index_for_debug << " successful" << endl;
  162. cerr << "Test " << test_index_for_debug++ << " Run Time: "
  163. << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
  164. cout << "--------------------------------------------------" << endl;
  165. }
  166. #else
  167. solve();
  168. #endif
  169. return 0;
  170. }

【bzoj2049】[Sdoi2008]Cave 洞穴勘测——线段树上bfs求可撤销并查集的更多相关文章

  1. BZOJ2049 SDOI2008 Cave 洞穴勘测 【LCT】

    BZOJ2049 SDOI2008 Cave 洞穴勘测 Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分 ...

  2. 【LCT】BZOJ2049 [SDOI2008]Cave 洞穴勘测

    2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 10059  Solved: 4863[Submit ...

  3. [BZOJ2049][Sdoi2008]Cave 洞穴勘测 LCT模板

    2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 9705  Solved: 4674[Submit] ...

  4. [bzoj2049][Sdoi2008]Cave 洞穴勘测_LCT

    Cave 洞穴勘测 bzoj-2049 Sdoi-2008 题目大意:维护一个数据结构,支持森林中加边,删边,求两点连通性.n个点,m个操作. 注释:$1\le n\le 10^4$,$1\le m\ ...

  5. [BZOJ2049] [SDOI2008] Cave 洞穴勘测 (LCT)

    Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...

  6. [bzoj2049][Sdoi2008]Cave 洞穴勘测——lct

    Brief Description 给定一个森林,您需要支持两种操作: 链接两个节点. 断开两个节点之间的链接. Algorithm Design 对于树上的操作,我们现在已经有了树链剖分可以处理这些 ...

  7. BZOJ2049: [Sdoi2008]Cave 洞穴勘测 Link-Cut-Tree 模板题

    传送门 搞了这么长时间Splay终于可以搞LCT了,等等,什么是LCT? $LCT$就是$Link-Cut-Tree$,是维护动态树的一个很高效的数据结构,每次修改和查询的均摊复杂度为$O(logN) ...

  8. BZOJ2049——[Sdoi2008]Cave 洞穴勘测

    1.题目大意:就是一个动态维护森林联通性的题 2.分析:lct模板题 #include <stack> #include <cstdio> #include <cstdl ...

  9. bzoj2049: [Sdoi2008]Cave 洞穴勘测

    lct入门题? 得换根了吧TAT 这大概不是很成熟的版本.. #include<iostream> #include<cstring> #include<cstdlib& ...

随机推荐

  1. Proto3:C++基本使用

    本教程提供protocol buffer在C++程序中的基础用法.通过创建一个简单的示例程序,向你展示如何: 在.proto中定义消息格式 使用protocol buffer编译器 使用C++ pro ...

  2. HTML笔记02

    网页中的颜色有三种表示方法 颜色单词:blue.green.red.yellow 10进制表示:rgb(255,0,0).rgb(0,255,0).rgb(0,0,255) 16进制表示:#ff000 ...

  3. VMware安装CentOS6.X 系统

    1.虚拟机中的"CD/DVD(IDE)"配置好Linux映像文件后,打开虚拟机,点击"开启此虚拟机" 2.进入光盘启动界面,选择第一项,表示安装升级Linux系 ...

  4. 量化投资学习笔记37——《Python机器学习应用》课程笔记10

    用KNN算法来进行数字识别,还是用sklearn自带的digits数据集. coding:utf-8 KNN算法实现手写识别 from sklearn import neighbors from sk ...

  5. echart 新手踩坑

    仪表盘踩坑 我采用的是单文件引入的方式来加载echarts图标也可以使用配置等方式详情参考文档,如果同学们要做出更加丰富的样式请参考文档配置手册配置手册:http://echarts.baidu.co ...

  6. 【django】 接收所有文件,前端展示文件(包括视频,文件,图片)ajax请求

    如果是后台上传文件: setting配置: STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ...

  7. 最适合初学者的一篇 Ribbon 教程

    什么是 Ribbon Ribbon 是一个基于 HTTP 和 TCP 的 客服端负载均衡工具,它是基于 Netflix Ribbon 实现的. 它不像 Spring Cloud 服务注册中心.配置中心 ...

  8. ASP.NET Core 快速入门(Razor Pages + Entity Framework Core)

    引子 自从 2009 年开始在博客园写文章,这是目前我写的最长的一篇文章了. 前前后后,我总共花了 5 天的时间,每天超过 3 小时不间断写作和代码调试.总共有 8 篇文章,每篇 5~6 个小结,总截 ...

  9. vs远程调试iis

    1.在开发电脑上 找到 D:\Software\VS2010\Common7\IDE\Remote Debugger 下面msvsmon.exe所在的两个文件夹x86和x64,使用x86或者x64是根 ...

  10. scrapy 在爬取过程中抓取下载图片

    先说前提,我不推荐在sarapy爬取过程中使用scrapy自带的 ImagesPipeline 进行下载,是在是太耗时间了 最好是保存,在使用其他方法下载 我这个是在 https://blog.csd ...