线段树区间离散化维护按秩合并并查集(可撤销)——牛客多校第八场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 ...
随机推荐
- 数据结构(c语言版,严蔚敏)第2章线性表
弟2章线性表
- JMeter生成性能报表-Windows环境和Linux环境
转载自https://www.cnblogs.com/imyalost/p/10239317.html
- 获取min-max之间的随机数
private static String getRandom(int min, int max){ Integer random =(int)(min+Math.random()*(max-min+ ...
- Robot Framework:接口测试
---恢复内容开始--- robotframework进行接口测试,需要安装Request和RequestLibrary包 pip install requests pip install -U ro ...
- 高级运维(二):搭建Nginx服务器、用户认证、基于域名的虚拟主机、SSL虚拟主机、Nginx反向代理
一.搭建Nginx服务器 目标: 在IP地址为192.168.4.5的主机上安装部署Nginx服务,并可以将Nginx服务器,要求编译时启用如下功能: 1> SSL加密功能 2> 设置Ng ...
- Spring Boot下启用https
1.需要一个证书,可以自己生成或者购买.下面是我们通过keytool自己生成. 打开运行,输入cmd,进入命令行 输入生成命令: keytool -genkey -alias tomcat -stor ...
- CentOS7简单安装mplayer和vlc!
http://pkgs.org/在这个网上搜索下面的包的最新版1. sudo rpm -ivh epel-release-7-0.2.noarch.rpm 2. sudo rpm -Uvh elrep ...
- ios获取软键盘完成事件
ios获取软键盘完成事件,通过判断input的onBlur事件即可
- 内存Zone中的pageset成员分析
1: struct per_cpu_pageset __percpu *pageset; 首先,分析一个函数,__free_pages,这个函数是Buddy System提供的API接口函数,用于翻译 ...
- PAT_A1080#Graduate Admission
Source: PAT A1080 Graduate Admission (30 分) Description: It is said that in 2011, there are about 10 ...