题意:

传送门

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

思路:

我们可以枚举每个编号,然后看看能通过这个编号的所有边能否构成一个图使得\(1\)走到\(n\),但是显然枚举点很不现实,那我们就枚举区间。

我们用左开右闭的线段树维护区间,然后让每个节点保存能覆盖当前区间的边的编号,然后遍历这个线段树。用可撤回的并查集维护当前的图,如果\(1\)和\(n\)在同一个图里,那显然当前的区间就能满足从\(1\)走到\(n\),那么就加上贡献。

代码:

#include<map>
#include<set>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const ull seed = 131;
const ll MOD = 1e9;
using namespace std;
vector<int> node[maxn << 2], vv;
int ans, n, m;
void build(int l, int r, int rt){
node[rt].clear();
if(l == r) return;
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
}
void update(int L, int R, int l, int r, int v, int rt){
if(L <= l && R >= r){
node[rt].push_back(v);
return;
}
int m = (l + r) >> 1;
if(L <= m)
update(L, R, l, m, v, rt << 1);
if(R > m)
update(L, R, m + 1, r, v, rt << 1 | 1);
}
struct qu{
int u, v, l, r;
}q[maxn];
int getid(int x){
return lower_bound(vv.begin(), vv.end(), x) - vv.begin() + 1;
} stack<pair<int, int> > instack; //合并的点和其父节点暂时增加的秩
int fa[maxn], dep[maxn];
int _find(int x){
return x == fa[x]? x : _find(fa[x]);
}
void Merge(int x, int y){
int fx = _find(x), fy = _find(y);
if(dep[fx] < dep[fy]){
fa[fx] = fy;
instack.push(make_pair(fx, 0));
}
else if(dep[fx] > dep[fy]){
fa[fy] = fx;
instack.push(make_pair(fy, 0));
}
else{
fa[fx] = fy;
dep[fy]++;
instack.push(make_pair(fx, 1));
}
}
void undo(){
pair<int, int> s = instack.top();
instack.pop();
dep[fa[s.first]] -= s.second;
fa[s.first] = s.first;
}
void dfs(int l, int r, int rt){
for(int i = 0; i < node[rt].size(); i++){
int u = q[node[rt][i]].u, v = q[node[rt][i]].v;
Merge(u, v);
}
if(_find(1) == _find(n)){
ans += vv[r] - vv[l - 1];
}
else if(l < r){
int m = (l + r) >> 1;
dfs(l, m, rt << 1);
dfs(m + 1, r, rt << 1 | 1);
}
for(int i = 0; i < node[rt].size(); i++){
undo();
}
}
int main(){
while(~scanf("%d%d", &n, &m)){
for(int i = 1; i <= m; i++){
scanf("%d%d%d%d", &q[i].u, &q[i].v, &q[i].l, &q[i].r);
vv.push_back(q[i].l), vv.push_back(q[i].r + 1);
}
sort(vv.begin(), vv.end());
vv.erase(unique(vv.begin(), vv.end()), vv.end()); build(1, vv.size(), 1);
for(int i = 1; i <= m; i++){
update(getid(q[i].l), getid(q[i].r + 1) - 1, 1, vv.size(), i, 1);
}
for(int i = 0; i <= n; i++) fa[i] = i;
while(!instack.empty()) instack.pop();
ans = 0;
dfs(1, vv.size(), 1);
printf("%d\n", ans);
}
return 0;
}

牛客多校第八场E Explorer(左开右闭线段树+可撤回并查集)题解的更多相关文章

  1. 2019牛客多校第七场E Find the median 权值线段树+离散化

    Find the median 题目链接: https://ac.nowcoder.com/acm/contest/887/E 题目描述 Let median of some array be the ...

  2. 2019牛客多校第八场 F题 Flowers 计算几何+线段树

    2019牛客多校第八场 F题 Flowers 先枚举出三角形内部的点D. 下面所说的旋转没有指明逆时针还是顺时针则是指逆时针旋转. 固定内部点的答案的获取 anti(A)anti(A)anti(A)或 ...

  3. 2020牛客多校第八场K题

    __int128(例题:2020牛客多校第八场K题) 题意: 有n道菜,第i道菜的利润为\(a_i\),且有\(b_i\)盘.你要按照下列要求给顾客上菜. 1.每位顾客至少有一道菜 2.给顾客上菜时, ...

  4. Distance(2019年牛客多校第八场D题+CDQ+树状数组)

    题目链接 传送门 思路 这个题在\(BZOJ\)上有个二维平面的版本(\(BZOJ2716\)天使玩偶),不过是权限题因此就不附带链接了,我也只是在算法进阶指南上看到过,那个题的写法是\(CDQ\), ...

  5. 暴力三维树状数组求曼哈顿距离求最值——牛客多校第八场D

    涉及的知识点挺多,但是大多是套路 1.求曼哈顿距离的最值一般对所有情况进行讨论 2.三维树状数组用来求前缀最大值 /* 有一个三维坐标系(x,y,z),取值范围为[1,n],[1,m],[1,h],有 ...

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

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

  7. 线段树区间离散化维护按秩合并并查集(可撤销)——牛客多校第八场E

    模板题..去网上学了可撤销的并查集.. /* 给定一个无向图,边的属性为(u,v,l,r),表示<u,v>可以通过的size为[l,r] 求出有多少不同的size可以从1->n 把每 ...

  8. 单调栈(最大子矩形强化版)——牛客多校第八场A

    求01矩阵里有多少个不同的1矩阵 首先预处理出pre[i][j]表示i上面连续的1个数,对每行的高度进行单调栈处理 栈里的元素维护两个值:pre[i][j]和向前延伸最多能维护的位置pos 然后算贡献 ...

  9. 牛客多校第八场 G Gemstones 栈/贪心

    题意: 对于一个序列,把可以把连着三个相同的字母拿走,问最多拿走多少组. 题解: 直接模拟栈,三个栈顶元素相同则答案+1,并弹出栈 #include<bits/stdc++.h> usin ...

随机推荐

  1. CF625E Frog Fights

    有\(n\)只青蛙在一个长度为\(m\)的环上打架:每只青蛙有一个初始位置\(p_i\),和一个跳跃数值\(a_i\).从\(1\)号青蛙开始按序号循环行动,每次若第\(i\)只青蛙行动,则它会向前跳 ...

  2. Python安装教程之anaconda篇

    [导读]我们知道,Python的功能非常强大.那么对于迫切想学习Python的新手同学来说,第一件事情可能需要了解python是什么?能用来做什么?语法结构是怎样的?这些我们几句话很难介绍清楚,后续会 ...

  3. 学习Java第三天

    方法重载:同一个类,方法名相同,参数不同(个数不同,类型不同,顺序不同),判断是否重载,只看方法名和参数,跟返回值无关. IDEA查看方法源代码:Crtl + 鼠标左键 进制表示 Java数值默认为十 ...

  4. MAX232数据方向

    在调试一个新板子的时候,串口调试从来都是放在前面的,而由于是一个新板子,电路图也是新的,因此有时候不知道串口线结对了没有,这个时候可能会对照PCB和原理图去看一下,但有时候看的人会很迷糊,因为不同的人 ...

  5. Oracle 常用命令大全(持续更新)

    数据库 ----数据库启动 & 关闭 启动数据库 SQL> startup nomount; SQL> alter database mount; SQL> alter da ...

  6. java基础-01代理类

    简单的代理类实现案例主实现类:ProxyTestimport java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;im ...

  7. widnows2008双网卡双ip不同Ip段

    机房内有不同段ip,因为线路不一样,比如普通带宽和cn2带宽,现有需求配置双网卡双ip ip1: 121.7*.*.*  255.255.255.192 121.7*.*129 ip2: 103.11 ...

  8. JAVA中关于基本数据和引用数据参数传递过程

    基本数据和引用数据参数传递过程 案例1:判断程序的输出结果 class Demo{ public static void main(String[] atgs){ int x =4; show(x); ...

  9. Java Object类 和 String类 常见问答 6k字+总结

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  10. 通过Joomla的两次RCE漏洞看session反序列化

    关于Session的前置知识: session 对数据的序列化方式一共有三种: 默认是 php 处理器:session.serialize_handler = php 效果如图: 通过|分割数据,|前 ...