模板题。。去网上学了可撤销的并查集。。

/*
给定一个无向图,边的属性为(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的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. Codeforces 1140F Extending Set of Points 线段树 + 按秩合并并查集 (看题解)

    Extending Set of Points 我们能发现, 如果把x轴y轴看成点, 那么答案就是在各个连通块里面的x轴的个数乘以y轴的个数之和. 然后就变成了一个并查集的问题, 但是这个题目里面有撤 ...

  7. 牛客多校第三场 G Removing Stones(分治+线段树)

    牛客多校第三场 G Removing Stones(分治+线段树) 题意: 给你n个数,问你有多少个长度不小于2的连续子序列,使得其中最大元素不大于所有元素和的一半 题解: 分治+线段树 线段树维护最 ...

  8. 牛客多校第四场sequence C (线段树+单调栈)

    牛客多校第四场sequence C (线段树+单调栈) 传送门:https://ac.nowcoder.com/acm/contest/884/C 题意: 求一个$\max {1 \leq l \le ...

  9. BZOJ 4668 冷战(按秩合并并查集+LCA)

    4668: 冷战 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 627  Solved: 303[Submit][Status][Discuss] D ...

随机推荐

  1. Buffering Data

    We keep telling you that you always need to close your files after you're done writing to them. Here ...

  2. CleanMyMac x如何关闭自动更新,CleanMyMac关闭自动更新的方法

    在众多Mac清理软件中,CleanMyMac x应该是大多数Mac用户的选择,因为它不仅可以帮助我们清理系统垃圾,还可以帮助我们彻底卸载Mac上不需要的应用程序.今天要给大家介绍的是CleanMyMa ...

  3. Java中的LinkedList

  4. Java——类之间的关系

    3.7 类之间的关系 3.7.1 泛化关系 类和类之间的继承关系及接口与接口之间的继承关系. 3.7.2 实现关系 类对接口的实现. 3.7.3 关联关系 类与类之间的连接,一个类可以知道另一个类的属 ...

  5. spring事件监听(eventListener)

    原理:观察者模式 spring的事件监听有三个部分组成,事件(ApplicationEvent).监听器(ApplicationListener)和事件发布操作. 事件 事件类需要继承Applicat ...

  6. 【dart学习】-- Dart之元数据

    一,概述 元数据概述  元数据(Metadata),又称中介数据.中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置.历 ...

  7. BZOJ 3430: [Usaco2014 Jan]Ski Course Rating(并查集+贪心)

    题面 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 136 Solved: 90 [Submit][Status][Discuss] Descript ...

  8. C++一些不常见的库及函数

    pbds库 平衡树:one , two #include <bits/extc++.h> using namespace std; using namespace __gnu_pbds; ...

  9. 2019牛客多校第三场H-Magic Line

    Magic Line 题目传送门 解题思路 因为坐标的范围只有正负1000,且所有点坐标都是整数,所以所有点相连构成的最大斜率只有2000,而我们能够输出的的坐标范围是正负10^9.所以我们先把这n个 ...

  10. 20140702 赋值构造函数的形参为什么一定用引用。string类的赋值运算函数的注意点

    1.复制构造函数为什么一定要用引用,而不是用值 类名::复制构造函数(类名&引用名) 传递引用,可以避免复制,如果一个数据相当大的化,进行复制会浪费很多时间的. 类名::复制构造函数(类名 变 ...