https://www.lydsy.com/JudgeOnline/problem.php?id=2049

线段树真神奇

题意:给出一波操作,拆边加边以及询问两点是否联通。

听说常规方法是在线LCT,留坑。

如果说这个删边的操作是删除上一条边,那这自然是可撤销并查集的模板题,直接在线维护就可以了。

但是问题在于删除边的顺序是不可能固定的,要知道并查集是不可以随意撤销的。

万万没想到还有更加高妙的手法。

首先可以证明一条边的存在一定是一段或者多段连续的区间。

建立一条时间节点长度的线段树,结点维护一个边集合,每个位置表示的是当前这个时间下存在了哪几条边。

将上述的边区间全部加入,和常规的线段树不一样,这个不需要lazy标记也不需要Pushdown到下属区间,为了节省时间和空间,对于1 - N区间的边来说,我们仅仅把1号结点加上这条边。

然后用dfs的方法,进入结点时加上这些边,离开的时候删除这些边,在线段树的叶子节点上,并查集维护的就是当前时间的状态,离线的query直接询问即可。

时间复杂度,加边的整个过程mlogm,询问的过程节点数mlogm * 并查集find操作logm = mlogm

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = ,f = ;char c = getchar();while (c<'' || c>''){if (c == '-') f = -;c = getchar();}
while (c >= ''&&c <= ''){x = x * + c - '';c = getchar();}return x*f;}
const double eps = 1e-;
const int maxn = 1e5 + ;
const int maxm = 2e6 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
struct Query{
int t,u,v;
Query(){}
Query(int t,int u,int v):t(t),u(u),v(v){}
}q[maxm];
struct Line{
int op,u,v;
Line(){}
Line(int op,int u,int v):op(op),u(u),v(v){}
}line[maxm];
map<PII,int>Q;
int Stack[maxm],top;
int size[maxn],fa[maxn];
void init(){
for(int i = ; i <= N ; i ++){
fa[i] = -; size[i] = ;
}
top = ;
}
//segment_tree
struct Tree{
int l,r;
int head;
}tree[maxm << ];
struct Edge{
PII data;
int next;
}edge[maxm << ];
int tot,cnt,cnt2;
void add(int u,PII v){
edge[tot].next = tree[u].head;
edge[tot].data = v;
tree[u].head = tot++;
}
void Build(int t,int l,int r){
tree[t].l = l; tree[t].r = r;
tree[t].head = -;
if(l == r) return;
int m = (l + r) >> ;
Build(t << ,l,m); Build(t << | ,m + ,r);
}
void update(int t,int l,int r,PII v){
if(l <= tree[t].l && tree[t].r <= r){
add(t,v);
return;
}
int m = (tree[t].l + tree[t].r) >> ;
if(r <= m) update(t << ,l,r,v);
else if(l > m) update(t << | ,l,r,v);
else{
update(t << ,l,m,v);
update(t << | ,m + ,r,v);
}
}
int find(int x){
while(fa[x] != -) x = fa[x];
return x;
}
void Union(int x,int y){
x = find(x); y = find(y);
if(x == y) return;
if(size[x] > size[y]) swap(x,y);
Stack[top++] = x;
fa[x] = y;
size[y] += size[x] + ;
}
void rewind(int t){
while(top > t){
int x = Stack[--top];
size[fa[x]] -= size[x] + ;
fa[x] = -;
}
}
void dfs(int t){
int now = top;
for(int i = tree[t].head; ~i; i = edge[i].next){
PII v = edge[i].data;
Union(v.fi,v.se);
}
if(tree[t].l == tree[t].r){
while(tot <= cnt2 && q[tot].t == tree[t].l){
if(find(q[tot].u) == find(q[tot].v)){
puts("Yes");
}else{
puts("No");
}
tot++;
}
rewind(now);
return;
}
dfs(t << ); dfs(t << | );
rewind(now);
}
int main(){
Sca2(N,M); init();
cnt = ,cnt2 = ;
for(int i = ; i <= M ; i ++){
char op[]; int u,v;
scanf("%s%d%d",op,&u,&v);
if(u > v) swap(u,v);
if(op[] == 'Q') q[++cnt2] = Query(cnt,u,v);
else if(op[] == 'C') line[++cnt] = Line(,u,v);
else line[++cnt] = Line(,u,v);
}
tot = ; Build(,,cnt);
for(int i = ; i <= cnt; i ++){
int &x = Q[mp(line[i].u,line[i].v)];
if(line[i].op == ) x = i;
else{
update(,x,i - ,mp(line[i].u,line[i].v));
x = ;
}
}
for(map<PII,int>::iterator it = Q.begin(); it != Q.end(); it++){
pair<PII,int> u = *it;
if(u.se) update(,u.se,cnt,u.fi);
}
tot = ;
dfs();
return ;
}

bzoj2049 线段树 + 可撤销并查集的更多相关文章

  1. Explorer(2019年牛客多校第八场E题+线段树+可撤销并查集)

    题目链接 传送门 题意 给你一张无向图,每条边\(u_i,v_i\)的权值范围为\([L_i,R_i]\),要经过这条边的条件是你的容量要在\([L_i,R_i]\),现在问你你有多少种容量使得你可以 ...

  2. 2019牛客暑期多校训练营(第八场) E 线段树+可撤销并查集

    题目传送门 题意: 给出m条无向边,每条边都有一个$[l,r]$,意思是体积在这个范围内的人才能通过这条边,询问有多少种体积的可能性,能使人从1到n 思路:由于是无向边,1和n的连通性可以用并查集维护 ...

  3. 【CF938G】Shortest Path Queries(线段树分治,并查集,线性基)

    [CF938G]Shortest Path Queries(线段树分治,并查集,线性基) 题面 CF 洛谷 题解 吼题啊. 对于每个边,我们用一个\(map\)维护它出现的时间, 发现询问单点,边的出 ...

  4. 【BZOJ4025】二分图(线段树分治,并查集)

    [BZOJ4025]二分图(线段树分治,并查集) 题面 BZOJ 题解 是一个二分图,等价于不存在奇环. 那么直接线段树分治,用并查集维护到达根节点的距离,只计算就好了. #include<io ...

  5. BZOJ 4025: 二分图 [线段树CDQ分治 并查集]

    4025: 二分图 题意:加入边,删除边,查询当前图是否为二分图 本来想练lct,然后发现了线段树分治的做法,感觉好厉害. lct做法的核心就是维护删除时间的最大生成树 首先口胡一个分块做法,和hno ...

  6. BZOJ 2333 棘手的操作(离线+线段树+带权并查集)

    这题搞了我一天啊...拍不出错原来是因为极限数据就RE了啊,竟然返回WA啊.我的线段树要开8倍才能过啊... 首先可以发现除了那个加边操作,其他的操作有点像线段树啊.如果我们把每次询问的联通块都放在一 ...

  7. 「CF319E」Ping-Pong「线段树」「并查集」

    题意 规定区间\((a,b)\)到区间\((c,d)\)有边当且仅当\(c<a<d\)或\(c<b<d\). 起初区间集合为空.有\(n\)(\(n\leq 10^5\))次操 ...

  8. 5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集

    LINK:波波老师 LINK:同bzoj 1396 识别子串 不过前者要求线性做法 后者可以log过.实际上前者也被我一个log给水过了. 其实不算很水 我自认跑的很快罢了. 都是求经过一个位置的最短 ...

  9. 牛客多校第八场E Explorer(左开右闭线段树+可撤回并查集)题解

    题意: 传送门 有\(n\)个点构成一个无向图,每条边有\(L_i,R_i\)表示这条边只能允许编号为\(L_i\dots R_i\)的人通过,现在问你最多有几个人能从\(1\)走到\(n\). 思路 ...

随机推荐

  1. WebAPI MVC Change Identity Default Table

    看过之前的文章小伙伴们应该已经明白了,当我们新建一个带有身份验证的模板时,会自带Identity Server,并且它的表名和字段名也都是默认的. 那么该如何修改它,并让EF知道呢?不废话,直接上代码 ...

  2. Android 模块化/热修复/插件化 框架选用

    概念汇总 动态加载:在程序运行的时候,加载一些程序自身原本不存在的文件并运行这些文件里的代码逻辑.动态加载是热修复与插件化实现的基础. 热修复:修改部分代码,不用重新发包,在用户不知情的情况下,给ap ...

  3. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  4. git errot

    常用 git 基础命令 1.错误信息 使用TortoiseGit执行pull命令时显示 git.exe pull --progress --no-rebase -v "origin" ...

  5. SHELL 脚本小技巧

    脚本很简单,直接上功能介绍及脚本,可以做模板使用: 记录日志,记录脚本开始执行时间.结束时间 usage 函数,脚本需接参数执行,避免误执行,告诉用户,这个脚本的使用方法 加锁,创建锁文件,脚本不允许 ...

  6. 反射中Class.forName()和classLoader的区别

    搞清楚两者之间区别前,我们来了解下类加载过程. 一.类加载过程 1.加载 通过一个类的全限定名来获取定义此类的二进制字节流. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构. 在内存中生 ...

  7. 深入浅出QOS详解(转)

    QOS学习笔记 (工作时间之余,总结了这些,累的食指快脱节了,现在还在恢复中,为的就是让文章质量再提高点,希望对大家有帮助!文章太长,为方便,我附件上文章原文.) QOS,服务质量.顾名思义,就是为了 ...

  8. prufer序列学习笔记

    prufer序列是一个定义在无根树上的东西. 构造方法是:每次选一个编号最小的叶子结点,把他的父亲的编号加入到序列的最后.然后删掉这个叶节点.直到最后只剩下两个节点,此时得到的序列就是prufer序列 ...

  9. python安装tesseract

    一.最近在学习python爬虫的时候需要用到tesseract,但书上的给的教程对我并不适用,坑了好久天,才终于成功. 二.方法: 1.由于我看的是静谧博主的那本书.他给的教程在python3安装有问 ...

  10. Flask Mysql数据库连接

    下载库: pip install flask-sqlalchemy 下载后进入终端使用python后import导入模块测试没有报错就说明成功了 py文件: # -*- encoding: utf-8 ...