Description

给定一个无向连通图和若干个小集合,每个小集合包含一些边,对于每个集合,你需要确定将集合中的边删掉后改图是否保持联通。集合间的询问相互独立

定义一个图为联通的当且仅当对于任意的两个顶点,都存在一条路径连接它们

Input

第一行为两个整数 \(n,m\),代表无向图的点数和边数

下面 \(m\) 行,包含两个整数 \(u,v\),代表该边连接点 \(u,v\)。第 \(i + 1\) 行的边的编号为 \(i\)。保证不存在重边和自环

下面一行包含一个整数 \(k\),表示集合个数

下面 \(k\) 行每行描述一个集合,每行的第一个数为集合中边的个数 \(c\),后面 \(c\) 个数代表集合内的边

Output

对于每个集合,输出一行代表去掉该集合中的边后图是否联通,如果联通输出 Connected,否则输出 Disconnected

Hint

\(1~\leq~n,k~\leq~10^5\)

\(1~\leq~m~\leq~2~\times~10^5\)

\(1~\leq~c~\leq~4\)

Solution

这个分治方法叫做线段树分治,而不是cdq分治。

我们考虑分治,设 \([l,r]\) 为区间 \([l,r]\) 中询问涉及边都没有被连上且其它边都连上时图是否联通,那么递归到底时即为一次询问的答案。

具体方法为先将没有被询问过的边连上。然后在递归区间 \([l,r]\) 时,将区间内 \([l,mid]\) 的询问中不涉及的边全部连上,递归处理左侧区间,然后撤销上次的连边,再将 \((mid, r]\) 中询问不涉及的边都连上,处理右边,回溯后再次撤销。在这个过程中用并查集维护一下联通块个数,在底部即可判断。存储需要撤销的操作可以用一个栈实现。

考虑时间复杂度:递归层数为 \(O(\log k)\),于是每个询问被处理 \(O(\log k)\) 次,对每个询问中的每条边,每被处理一次就会有一次并查集的删除和撤销操作,由于是按秩合并实现,单次操作复杂度 \(O(\log n)\)。所以单个询问的复杂度为 \(O(c~\log n)\)。共有 \(k\) 个操作,于是总的时间复杂度为 \(O(k~\times~c~\times~\log k~\log n)\)。

这个分治和cdq分治的区别大概在于……此分治时 mid 两侧的区间是被 “平等对待” 的,即记录右边区间对左边区间的贡献,但是cdq分治不是。

另外上面做线段树分治的时候并没有将树显式的建出来,但他们在本质上是一样的。

另外还有一种神仙做法在这里

Code

#include <cstdio>
#include <vector>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif typedef long long int ll; namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
} template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
} namespace OPT {
char buf[120];
} template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
} const int maxn = 100010;
const int maxm = 200010; struct Edge {
int from, to;
};
Edge edge[maxm]; struct Ask {
int c;
int mu[5];
};
Ask ask[maxn]; struct OP {
int a, b, p;
};
OP stack[maxm * 20]; int n, m, k, cnt, top;
int ufs[maxn], rk[maxn];
bool vis[maxm]; void reset();
int find(int);
void cont(int, bool);
void unionn(int, int, bool);
void work(int, int, std::vector<int>&);
void divide(int, int, std::vector<int>&); int main() {
freopen("1.in", "r", stdin);
qr(n); qr(m); cnt = n;
for (int i = 1; i <= m; ++i) {
qr(edge[i].from); qr(edge[i].to);
}
qr(k);
for (int i = 1; i <= k; ++i) {
qr(ask[i].c);
for (int j = 1; j <= ask[i].c; ++j) {qr(ask[i].mu[j]); vis[ask[i].mu[j]] = true;}
}
for (int i = 1; i <= n; ++i) ufs[i] = i, rk[i] = 1;
std::vector<int>dn;
for (int i = 1; i <= m; ++i)
if (!vis[i]) cont(i, false);
else dn.push_back(i);
work(1, k, dn);
return 0;
} void work(int l, int r, std::vector<int>&e) {
for (int i = l; i <= r; ++i) {
for (int j = 1; j <= ask[i].c; ++j) vis[ask[i].mu[j]] = true;
}
std::vector<int>dn;
int _tp = top;
for (int i = 0, len = e.size(); i < len; ++i) {
if (!vis[i]) cont(i, true);
else dn.push_back(i);
}
for (int i = l; i <= r; ++i) {
for (int j = 1; j <= ask[i].c; ++j) vis[ask[i].mu[j]] = false;
}
divide(l, r, dn);
while (top != _tp) reset();
} void divide(int l, int r, std::vector<int>&e) {
if (l == r) {
puts(cnt == 1 ? "Connected" : "Disconnected");
return;
}
int mid = (l + r) >> 1;
work(l, mid, e);
work(mid + 1, r, e);
} int find(int x) {return ufs[x] == x ? x : find(ufs[x]);} void cont(int x, bool rec) {
int fa = find(edge[x].from), fb = find(edge[x].to);
if (fa != fb) {
unionn(fa, fb, rec);
--cnt;
}
} void unionn(int a, int b, bool rec) {
int c;
if (rk[a] < rk[b]) {c = 1; ufs[a] = b;}
else if (rk[a] > rk[b]) {c = 2; ufs[b] = a;}
else {c = 3; ufs[b] = a; ++rk[a];}
if (rec) stack[++top] = {a, b, c};
} void reset() {
int a = stack[top].a, b = stack[top].b, p = stack[top--].p;
switch (p) {
case 1:
ufs[a] = a; break;
case 2:
ufs[b] = b; break;
case 3:
ufs[b] = b; --rk[a]; break;
}
++cnt;
}

【线段树分治】【P5227】 [AHOI2013]连通图的更多相关文章

  1. 线段树分治初步学习&洛谷P5227[AHOI2013]连通图

    线段树分治 其实思想说起来是比较简单的,我们把这个题里的所有操作(比如连边删边查询balabala)全部拍到一棵线段树上,然后对着整棵树dfs一下求解答案,顺便把操作做一下,回溯的时候撤销一下即可.虽 ...

  2. BZOJ3237:[AHOI2013]连通图(线段树分治,并查集)

    Description Input Output Sample Input 4 5 1 2 2 3 3 4 4 1 2 4 3 1 5 2 2 3 2 1 2 Sample Output Connec ...

  3. BZOJ3237 AHOI2013连通图(线段树分治+并查集)

    把查询看做是在一条时间轴上.那么每条边都有几段存在时间.于是线段树分治就好了. 然而在bzoj上t掉了,不知道是常数大了还是写挂了. 以及brk不知道是啥做数组名过不了编译. #include< ...

  4. 3237: [Ahoi2013]连通图 线段树分治

    题解: cf765f cf671e bzoj4184 bzoj4552 线段树分治裸题 还是介绍一下线段树分治 这个东西其实挺简单但也挺有用的 可以把删除+插入操作变成只有插入(倒着就是删除) 像这一 ...

  5. 线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

    闲话 stO猫锟学长,满脑子神仙DS 网上有不少Dalao把线段树分治也归入CDQ分治? 还是听听YCB巨佬的介绍: 狭义:只计算左边对右边的贡献. 广义:只计算外部对内部的贡献. 看来可以理解为广义 ...

  6. 【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横

    不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路 ...

  7. LOJ2312 LUOGU-P3733「HAOI2017」八纵八横 (异或线性基、生成树、线段树分治)

    八纵八横 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路的两端都是城市(可能两端是同一个 ...

  8. loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)

    题意 题目链接 Sol 线性基+线段树分治板子题.. 调起来有点自闭.. #include<bits/stdc++.h> #define fi first #define se secon ...

  9. BZOJ.4184.shallot(线段树分治 线性基)

    BZOJ 裸的线段树分治+线性基,就是跑的巨慢_(:з」∠)_ . 不知道他们都写的什么=-= //41652kb 11920ms #include <map> #include < ...

随机推荐

  1. Spark概述及集群部署

    Spark概述 什么是Spark (官网:http://spark.apache.org) Spark是一种快速.通用.可扩展的大数据分析引擎,2009年诞生于加州大学伯克利分校AMPLab,2010 ...

  2. 时间戳使用的bug,你见过么

    博主本人前几天给公司项目里写了个禁言和解除禁言的功能,项目中涉及到对时间的处理,因为学得时候也没很注意,就按自己的思路去写了,运行起来发现了一个天大的bug,就是写的延后一年尽然,刚开始就执行了,而且 ...

  3. rev命令详解

    基础命令学习目录首页 rev命令将文件中的每行内容以字符为单位反序输出,即第一个字符最后输出,最后一个字符最先输出,依次类推. #cat a.txt wo shi mcw, nihao how do ...

  4. uptime命令详解

    基础命令学习目录首页 users个数和窗口数一致 原文链接:https://www.cnblogs.com/ultranms/p/9253217.html uptime 另外还有一个参数 -V(大写) ...

  5. Python基础_内置函数

        Built-in Functions     abs() delattr() hash() memoryview() set() all() dict() help() min() setat ...

  6. jenkins设置定时任务

    每次都手动的构建项目显然不够方便,有时候需要定时地执行自动化测试脚本.例如,每天晚上定时执行 pjenkins.py 文件来运行自动化测试项目. 设置定时任务 前面已经创建的 “python test ...

  7. JAVA第一次实验 ——凯撒密码的实现

    JAVA实验一   编写程序实现凯撒密码 201352330 潘俊洋 一.实验说明 凯撒密码作为一种最为古老的对称加密体制,在古罗马的时候都已经很流行,他的基本思想是:通过把字母移动一定的位数来实现加 ...

  8. NetFPGA-SUME下reference_nic测试

    Reference_nic Reference_nic是NetFPGA-SUME中提供的一个参考Demo,本文主要介绍如何构建并在SUME上运行reference_nic. GIT源 git clon ...

  9. css布局 弹性布局 和 网格布局

    这里就不写这两种布局的内容了 弹性布局链接:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html 网格布局链接:https://www.ji ...

  10. NodeJs实现客户端登陆

    nodejs的api中有一个process进程对象,process 对象是一个 global (全局变量),提供有关信息,控制当前 Node.js 进程.作为一个对象,它对于 Node.js 应用程序 ...