original link - https://ac.nowcoder.com/acm/contest/888/E
题意:
给出n个点,m条边,每条边上有个范围,指定范围的数字才能通过。现在点 1 1 1上有 [ 1 , 1 e 9 ] [1,1e9] [1,1e9]想要到达点 n n n,问最后有多少个点可以到达。
解析:
直接搜应该是不行地,我们考虑分治答案。
分治区间 [ L , R ] [L,R] [L,R],将所有包含该区间的边都连上,查看是否可以从 1 → n 1\to n 1→n。这个可以用并查集来查询。
如果可以,说明 [ L , R ] [L,R] [L,R]可以计入答案;否则,我们去判断 [ L , m i d ] , [ m i d + 1 , R ] [L,mid],[mid+1,R] [L,mid],[mid+1,R]。
tip:
- 并查集因为在回溯时要拆开,所以不能路径压缩,所以需要启发式合并。
- 我们需要将所有边加入对应的可行集合(包含 [ L , R ] [L,R] [L,R]后,加入 [ L , R ] [L,R] [L,R],之后就不用加入 [ L , m i d ] [L,mid] [L,mid])
- 区间加离散化,所以要将区间值存到点上,这个操作为: R + 1 , [ g e t ( L ) , g e t ( R + 1 ) − 1 ] R+1,[get(L),get(R+1)-1] R+1,[get(L),get(R+1)−1]( g e t get get离散化 i n d e x index index)
代码:
#include
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int maxn=2e5+5;
int n,m;
struct Uni{
int num;
int a[maxn];
void push(int p){
a[++num]=p;
}
void init(){
sort(a+1,a+1+num);
num=unique(a+1,a+1+num)-a-1;
}
int pos(int val){
return lower_bound(a+1,a+1+num,val)-a;
}
}U;
struct edge{
int l,r,u,v;
}e[maxn];
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
vector<int>tr[maxn<<2];
void insert(int rt,int l,int r,int L,int R,int id){
if(l>=L&&r<=R){
tr[rt].push_back(id);
return ;
}
if(L<=mid)
insert(ls,l,mid,L,R,id);
if(R>mid)
insert(rs,mid+1,r,L,R,id);
}
#define pill pair
namespace link{
int fa[maxn];
int siz[maxn];
void init(int n){
rep(i,1,n)fa[i]=i,siz[i]=1;
}
int fin(int a){
return fa[a]==a?a:fin(fa[a]);
}
stack<pill>sta;
bool un(int a,int b){
int f1=fin(a),f2=fin(b);
if(f1==f2)return 0;
if(siz[f1]>siz[f2])swap(f1,f2);
fa[f1]=f2;
sta.push({f1,f2});
siz[f2]+=siz[f1];
return 1;
}
void undo(int tim){
while(tim--){
auto p=sta.top();sta.pop();
siz[p.second]-=siz[p.first];
fa[p.first]=p.first;
}
}
}
using namespace link;
int ans;
void query(int rt,int l,int r){ // 对每个段判断是否可行
int ct=0;
for(auto i:tr[rt]){
int u=e[i].u,v=e[i].v;
un(u,v)&&++ct;
}
if(fin(1)==fin(n)){
ans+=U.a[r+1]-U.a[l];
}
else if(l==r){
;
}
else{
query(ls,l,mid);
query(rs,mid+1,r);
}
undo(ct);
}
int main(){
scanf("%d%d",&n,&m);
U.num=0;
rep(i,1,m){
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].r);
U.push(e[i].l);
U.push(e[i].r+1);
}
U.init();
rep(i,1,m){
e[i].l=U.pos(e[i].l);
e[i].r=U.pos(e[i].r+1)-1;
insert(1,1,U.num,e[i].l,e[i].r,i);
}
init(n);
query(1,1,U.num);
printf("%d\n",ans);
}