线段树区间离散化维护按秩合并并查集(可撤销)——牛客多校第八场E
模板题。。去网上学了可撤销的并查集。。
/*
给定一个无向图,边的属性为(u,v,l,r),表示<u,v>可以通过的size为[l,r]
求出有多少不同的size可以从1->n
把每条边的范围[l,r]进行区间离散化然后 建立线段树,然后把每条边按范围更新进线段树里
对线段树进行dfs,同时维护一个可撤销的并查集,经过每个线段树结点都用结点里存的边去更新并查集
到了叶子结点,如果发现[1,n]在同一个集合里,说明联通,那么把这个区间的贡献算上
回溯时要对并查集进行撤销
*/
#include<bits/stdc++.h>
#include<vector>
using namespace std;
#define maxn 200005
typedef pair<int,int>pii;
struct Edge{int u,v,l,r;}e[maxn];
int n,m,x[maxn],tot,ans; stack<pii>stk;//合并操作栈
int F[maxn],size[maxn];
int find(int x){//这里不能路径压缩
return F[x]==x?x:find(F[x]);
}
void bing(int x,int y){
int f1=find(x),f2=find(y);
if(f1==f2)//压入无效操作
stk.push(make_pair(-,-));
else {//把f2并入f1
if(size[f1]<size[f2])swap(f1,f2);
size[f1]+=size[f2];
F[f2]=f1;
stk.push(make_pair(f1,f2));
}
}
void cancle(){
pii t=stk.top();stk.pop();
if(t.first==- && t.second==-)return;
size[t.first]-=size[t.second];
F[t.second]=t.second;
return;
} #define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
vector<int>seg[maxn<<];
void update(int L,int R,int id,int l,int r,int rt){
if(L<=l && R>=r){seg[rt].push_back(id);return;}
int m=l+r>>;
if(L<=m)update(L,R,id,lson);
if(R>m)update(L,R,id,rson);
}
void query(int l,int r,int rt){
for(int i=;i<seg[rt].size();i++){
int j=seg[rt][i];
bing(e[j].u,e[j].v);
}
if(l==r){
if(find()==find(n))
ans+=x[l+]-x[l];
for(int i=;i<seg[rt].size();i++)cancle();
return;
}
int m=l+r>>;
query(lson);query(rson);
for(int i=;i<seg[rt].size();i++)cancle();
} int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].r);
for(int i=;i<=m;i++){
x[++tot]=e[i].l;
x[++tot]=++e[i].r;
}
sort(x+,x++tot);
tot=unique(x+,x++tot)-x-; for(int i=;i<=m;i++){
int posl=lower_bound(x+,x++tot,e[i].l)-x;
int posr=lower_bound(x+,x++tot,e[i].r)-x;posr--;
update(posl,posr,i,,tot,);
} for(int i=;i<=n;i++)F[i]=i;
for(int i=;i<=n;i++)size[i]=;
query(,tot,);
cout<<ans<<'\n';
}
下面是比较简洁的代码
#include<bits/stdc++.h>
#include<vector>
using namespace std;
#define maxn 400005
typedef pair<int,int>pii; int n,m,x[maxn],tot;
long long ans;
int u[maxn],v[maxn],L[maxn],R[maxn],num[maxn]; stack<pii>stk;//合并操作栈
int fa[maxn],sz[maxn];
int find(int x){//这里不能路径压缩
return fa[x]==x?x:find(fa[x]);
}
void bing(int x,int y){
int f1=find(x),f2=find(y);
if(f1==f2)//压入无效操作
stk.push(make_pair(-,-));
else {//把f2并入f1
if(sz[f1]<sz[f2])swap(f1,f2);
sz[f1]+=sz[f2];
fa[f2]=f1;
stk.push(make_pair(f1,f2));
}
}
void cancel(){
pii t=stk.top();stk.pop();
if(t.first==- && t.second==-)return;
sz[t.first]-=sz[t.second];
fa[t.second]=t.second;
} #define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
vector<int>seg[maxn<<];
void update(int L,int R,int id,int l,int r,int rt){
if(L<=l && R>=r){seg[rt].push_back(id);return;}
int m=l+r>>;
if(L<=m)update(L,R,id,lson);
if(R>m)update(L,R,id,rson);
}
void query(int l,int r,int rt){
for(int i=;i<seg[rt].size();i++){
int j=seg[rt][i];
bing(u[j],v[j]);
}
if(l==r){
if(find()==find(n))
ans+=num[l+]-num[l];
for(int i=;i<seg[rt].size();i++)cancel();
return;
}
int m=l+r>>;
query(lson);query(rson);
for(int i=;i<seg[rt].size();i++)cancel();
}
int main(){
cin>>n>>m;
for(int i=;i<=m;i++){
cin>>u[i]>>v[i]>>L[i]>>R[i];
num[++tot]=L[i];
num[++tot]=++R[i];
}
sort(num+,num++tot);
tot=unique(num+,num++tot)-num-; for(int i=;i<=m;i++){
int posl=lower_bound(num+,num++tot,L[i])-num;
int posr=lower_bound(num+,num++tot,R[i])-num-;
update(posl,posr,i,,tot-,);
}
for(int i=;i<=n;i++)fa[i]=i,sz[i]=;
query(,tot-,);
cout<<ans<<endl;
}
线段树区间离散化维护按秩合并并查集(可撤销)——牛客多校第八场E的更多相关文章
- 2019牛客多校第八场 F题 Flowers 计算几何+线段树
2019牛客多校第八场 F题 Flowers 先枚举出三角形内部的点D. 下面所说的旋转没有指明逆时针还是顺时针则是指逆时针旋转. 固定内部点的答案的获取 anti(A)anti(A)anti(A)或 ...
- 牛客多校第八场E Explorer(左开右闭线段树+可撤回并查集)题解
题意: 传送门 有\(n\)个点构成一个无向图,每条边有\(L_i,R_i\)表示这条边只能允许编号为\(L_i\dots R_i\)的人通过,现在问你最多有几个人能从\(1\)走到\(n\). 思路 ...
- Explorer(2019年牛客多校第八场E题+线段树+可撤销并查集)
题目链接 传送门 题意 给你一张无向图,每条边\(u_i,v_i\)的权值范围为\([L_i,R_i]\),要经过这条边的条件是你的容量要在\([L_i,R_i]\),现在问你你有多少种容量使得你可以 ...
- Distance(2019年牛客多校第八场D题+CDQ+树状数组)
题目链接 传送门 思路 这个题在\(BZOJ\)上有个二维平面的版本(\(BZOJ2716\)天使玩偶),不过是权限题因此就不附带链接了,我也只是在算法进阶指南上看到过,那个题的写法是\(CDQ\), ...
- 暴力三维树状数组求曼哈顿距离求最值——牛客多校第八场D
涉及的知识点挺多,但是大多是套路 1.求曼哈顿距离的最值一般对所有情况进行讨论 2.三维树状数组用来求前缀最大值 /* 有一个三维坐标系(x,y,z),取值范围为[1,n],[1,m],[1,h],有 ...
- Codeforces 1140F Extending Set of Points 线段树 + 按秩合并并查集 (看题解)
Extending Set of Points 我们能发现, 如果把x轴y轴看成点, 那么答案就是在各个连通块里面的x轴的个数乘以y轴的个数之和. 然后就变成了一个并查集的问题, 但是这个题目里面有撤 ...
- 牛客多校第三场 G Removing Stones(分治+线段树)
牛客多校第三场 G Removing Stones(分治+线段树) 题意: 给你n个数,问你有多少个长度不小于2的连续子序列,使得其中最大元素不大于所有元素和的一半 题解: 分治+线段树 线段树维护最 ...
- 牛客多校第四场sequence C (线段树+单调栈)
牛客多校第四场sequence C (线段树+单调栈) 传送门:https://ac.nowcoder.com/acm/contest/884/C 题意: 求一个$\max {1 \leq l \le ...
- BZOJ 4668 冷战(按秩合并并查集+LCA)
4668: 冷战 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 627 Solved: 303[Submit][Status][Discuss] D ...
随机推荐
- mybatis 多表查询sql
在使用spring,spring mvc,mybatis时,mybatis链接数据库做多表查询的时候,sql语句中直接使用left join等链接字符就可以 链接多个表,参数类型是parameterT ...
- 【LeetCode 3】无重复字符的最长子串
描述 [题解] 尺取法 对于[l..r]这段 如果新加进来的s[r]有和之前的重复. 那么就不用重新开始了. 直接递增左区间. 让这段没有重复数字了再说. *****然后再把s[r]加进去. [代码] ...
- Android中.9图片的了解和制作过程
个部分(九宫格),分别为4个角,4条边,以及一个中间区域,4个角是不做拉升的,所以还能一直保持圆角的清晰状态,而2条水平边和垂直边分别只做水平和垂直拉伸,所以不会出现边会被拉粗的情况,只有中间用黑线指 ...
- mysql笔试题大餐---1、组合查询方式及having
mysql笔试题大餐---1.组合查询方式及having 一.总结 一句话总结: 实践:我之前的mysql真的学的太浅了,这种情况下,依据实践(做题)才是唯一能把它学好的方式 学的暂时够了,以实践而学 ...
- PHP面试 PHP基础知识 十(网络协议)
网络协议 HTTP协议状态码 状态分为五大类:1XX.2XX.3XX.4XX.5XX 1XX:信息类状态码 表示接受请求正在处理 2XX:success 成功状态码 请求正常处理完毕 3XX:重定 ...
- 3.2 Redux TodoApp
上一节讲完了 redux 中的概念,但是仍然没有和 react 联系起来,这一节将利用 redux 在 react 中实现完整的 todolist: 在 react 使用 redux 通过 Provi ...
- docker container 的操作
删除所有退出的容器 docker container rm $(docker ps -aq)
- 人工智能都能写Java了!这款插件让你编程更轻松
最近在浏览技术社区,发现了一款 IDE 插件,利用人工智能技术帮助程序员高效写代码.节省开发时间,一下子勾起了我的好奇心. 下载之后,使用一番,确实蛮好的,可以有效提升编程效率. 这款插件叫:aixc ...
- leetcode.字符串.5最长回文子串-Java
1. 具体题目 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 1000. 示例 1: 输入: "babad" 输出: "bab" ...
- [已解决]报错Could not install packages due to an EnvironmentError
安装OpenCV过程中出现错误 代码: pip-conda install -i https://pypi.douban.com/simple/ opencv-python 报错内容如下: Could ...