2019牛客第八场多校 E_Explorer 可撤销并查集(栈)+线段树
@
题意:
题目类似:CF366D,Gym101652T
本题给你\(n(100000)\)个点\(m(10000)\)条边,每无向边允许通过编号在\([Li,Ri](1\le Li\le Ri\le 1e9)\)内的人,问从\(1\)到\(n\)能通过多少个人。
分析:
赛中艹了30多发暴力无济于事。。。
因为以前做过一道数据范围1000的原题,当时做法好像是离散化后枚举区间暴力跑\(dfs\)查询或者带着区间跑暴力\(BFS\)最后检查一遍。。。
流下了菜鸡的泪水,主要是思维受限了,这很致命。
听说这题可以LCT写,目前只会可撤销并查集+线段树的写法。。
- 上面讲的也很清楚了,把所有的权值离散化一下,然后建一个以权值为关键字的线段树
- 这里提一下,这个线段树和上一场牛客的E题一模一样,是左闭右开线段树,就是每个叶子节点代表的是一段权值区间。这个线段树的解释:here
- 然后线段树上每个节点存的是覆盖当前点的权值区间的所有边。
- 区间更新完之后,把整颗树遍历一遍得出答案。
- 对于从根到叶子节点这段的权值区间中包含的边,用一个并查集维护连通性。
- 回溯时要撤销上面几条边的影响。记录一下合并上一条边的两个根,然后还原他们的\(fa[],rnk[]\)就行。
- 用一个栈记录加入的边即可,后进先出的结构正适合本题的撤销操作。还有,可撤销并查集不能路径压缩,必须按秩合并。
Code
const int MXN = 3e3 + 7;
const int MXE = 2e6 + 7;
int n, m;
vector<int> sum[MXN<<2], vs;
int fa[MXN], rnk[MXN], top;
struct lh {
int fi, se, u, v;
}stak[MXN];
struct lp {
int u, v, l ,r;
}cw[MXN];
void update(int L, int R, int v, int l ,int r, int rt) {
if(L <= l && r <= R) {
sum[rt].eb(v);
return;
}
int mid = (l + r) >> 1;
if(L > mid) update(L, R, v, mid + 1, r, rt<<1|1);
else if(R <= mid) update(L, R, v, l, mid, rt<<1);
else {
update(L, mid, v, l, mid, rt<<1), update(mid + 1, R, v, mid + 1, r, rt<<1|1);
}
}
int Fi(int x) {
return fa[x] == x? x: Fi(fa[x]);
}
int pa, pb, ans;
void build(int l, int r, int rt) {
if(l == r) {
// debug(l, r, Fi(3), Fi(5))
for(auto x: sum[rt]) {
pa = Fi(cw[x].u), pb = Fi(cw[x].v);
if(pa == pb) continue;
// debug(l, r, cw[x].u, cw[x].v)
if(rnk[pa] > rnk[pb]) swap(pa, pb);
fa[pa] = pb;
rnk[pb] += rnk[pa];
stak[++ top] = {rt, x, pa, pb};
}
if(Fi(1) == Fi(n)) ans += vs[l+1] - vs[l];
while(top && stak[top].fi == rt) {//撤销
int x = stak[top].se;
if(rnk[stak[top].u] > rnk[stak[top].v]) {
fa[stak[top].v] = stak[top].v;
rnk[stak[top].u] -= rnk[stak[top].v];
}else {
fa[stak[top].u] = stak[top].u;
rnk[stak[top].v] -= rnk[stak[top].u];
}
-- top;
}
// debug(l, r, Fi(3), Fi(5))
return;
}
int mid = (l + r) >> 1;
// debug(l, r, mid, rt)
for(auto x: sum[rt]) {
pa = Fi(cw[x].u), pb = Fi(cw[x].v);
if(pa == pb) continue;
// debug(pa, pb)
if(rnk[pa] > rnk[pb]) swap(pa, pb);
fa[pa] = pb;
rnk[pb] += rnk[pa];
stak[++ top] = {rt, x, pa, pb};
}
build(l, mid, rt << 1), build(mid + 1, r, rt << 1 | 1);
while(top && stak[top].fi == rt) {//撤销
int x = stak[top].se;
if(rnk[stak[top].u] > rnk[stak[top].v]) {
fa[stak[top].v] = stak[top].v;
rnk[stak[top].u] -= rnk[stak[top].v];
}else {
fa[stak[top].u] = stak[top].u;
rnk[stak[top].v] -= rnk[stak[top].u];
}
-- top;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
// freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
n = read(), m = read();
for(int i = 1; i <= n; ++i) fa[i] = i, rnk[i] = 1;
vs.eb(0);
for(int i = 1; i <= m; ++i) {
cw[i].u =read(), cw[i].v = read();cw[i].l = read(), cw[i].r = read();
vs.eb(cw[i].l), vs.eb(cw[i].r + 1);
}
my_unique(vs);
// for(auto x: vs) printf("%d ", x); printf("\n");
for(int i = 1; i <= m; ++i) {
update(lower_bound(all(vs), cw[i].l) - vs.begin(), upper_bound(all(vs), cw[i].r) - vs.begin() - 1, i, 1, vs.size() - 1, 1);
// debug(lower_bound(all(vs), cw[i].l) - vs.begin(), upper_bound(all(vs), cw[i].r) - vs.begin() - 1, cw[i].u, cw[i].v);
}
build(1, vs.size() - 1, 1);
printf("%d\n", ans);
return 0;
}
两道待补练习题:CF 813F 可撤销并查集+分治,BZOJ 3237 CDQ分治+带撤销并查集
复习带权并查集和可持久化并查集。
2019牛客第八场多校 E_Explorer 可撤销并查集(栈)+线段树的更多相关文章
- 2019牛客第八场多校 D_Distance 三维BIT或定期重建套路
目录 题意: 分析: @(2019牛客暑期多校训练营(第八场)D_Distance) 题意: 在三维空间\((n\times m\times h\le 100000)\)内,有\(q(q\le 100 ...
- 牛客第三场多校 E Sort String
链接:https://www.nowcoder.com/acm/contest/141/E来源:牛客网 Eddy likes to play with string which is a sequen ...
- 牛客第三场多校 H Diff-prime Pairs
链接:https://www.nowcoder.com/acm/contest/141/H来源:牛客网 Eddy has solved lots of problem involving calcul ...
- 牛客第五场多校 J plan 思维
链接:https://www.nowcoder.com/acm/contest/143/J来源:牛客网 There are n students going to travel. And hotel ...
- 牛客第五场多校 A gpa 分数规划(模板)
链接:https://www.nowcoder.com/acm/contest/143/A来源:牛客网 Kanade selected n courses in the university. The ...
- 牛客小白月赛12 H 华华和月月种树 (离线dfs序+线段树)
链接:https://ac.nowcoder.com/acm/contest/392/H 来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 131072K,其他语言2621 ...
- PACM Team(牛客第三场多校赛+dp+卡内存+打印路径)
题目链接(貌似未报名的不能进去):https://www.nowcoder.com/acm/contest/141/A 题目: 题意:背包题意,并打印路径. 思路:正常背包思路,不过五维的dp很容易爆 ...
- 牛客第八场 C-counting paths 树形dp计数
题目地址 题意 给你一颗树 初始点颜色全部为白色 对于每一个满足要求一的点集s f(s)的定义为先把点集内的点染黑 满足要求二的路径集合数量 要求一为两两黑点之间不能出现白色的点 要求二为将这个路径集 ...
- 2020牛客寒假算法基础集训营3 - G. 牛牛的Link Power II(线段树)
题目链接:牛牛的Link Power II 题意:给你一个只含$0$和$1$的串,定义串的$Link$值为串中两个的$1$之间的距离的和,$(u,v)$和$(v,u)$被看认为是同一对,有$m$次操作 ...
随机推荐
- 踩坑记之字体图标在webpack中
首先介绍一个还不错的字体图标库: font-awesome 是在发现bootstrap在npm下载后没有font包以后重新找的字体库,内容还挺丰富的,好像还有动态图,感兴趣的可以看一下 我的环境是we ...
- oracle 远程访问
oracle 本机能连外部访问不了 新装的 oracle 数据库经常会出现本地计算机能连接,但是局域网内的其他计算机不能连接的问题,如果出现此问题可以参考此文来解决. 本文中用的数据库版本为 Orac ...
- Git-学习开源代码的技巧
从最初提交开始学习每次提交的代码 https://stackoverflow.com/questions/5630110/how-to-read-source-code-using-git 很久以前就 ...
- Java + selenium 元素定位(2)之By LinkText/PartialLinkText
本章介绍的两种方法都是对于网页上的文字链接的定位操作.根据名字,我们就可以看出来,这两者其实很相似,那么他们的不同在哪里呢. By LinkText()方法,是对一个的网页超链接,我们所需要输入的关键 ...
- Java +selenium Navigation接口介绍
Navigation接口主要实现对浏览器的前进.后退.打开网址.刷新当前页面等操作的. void back():就是操作当前页面后退,相当于网页的后退按钮. void forward():就是操作当前 ...
- vue-router 传递参数的几种方式
本文转载自:https://blog.csdn.net/crazywoniu/article/details/80942642 vue-router传递参数分为两大类 编程式的导航 router.pu ...
- 数据库(四)—— Redis数据库
目录 redis 一.redis的安装部署 二.redis配置文件 1.基本配置 2.安全配置 3.查看配置信息 三.redis持久化 1.持久化类型 2.RDB持久化 3.AOF持久化(append ...
- JavaScript-打开新窗口(window.open)和 关闭窗口(window.close)
JavaScript-打开新窗口 open() 方法可以查找一个已经存在或者新建的浏览器窗口. 语法: window.open([URL], [窗口名称], [参数字符串]) 参数说明: URL:可选 ...
- windows 安装多个mysql
安装多个mysql,其实很简单,网上资料也很多,我整理一下,也跟着来凑个热闹. 1.下载mysql zip 解压到指定目录,我这边就3个,更多也类似 d:\mysql1 d:\mysql2 d:\my ...
- Eclipse插件pydev编辑.py文件时报错:unresolved import error.解决办法
在同一个包中import还报unresolved import error.感觉很奇怪,原来需要把当前的包也要添加到System libs中