【题解】
题意:给定一个无向图,有n个点m条边,每条边有四个属性u,v,l,r,表示连接u,v的这条边允许通过的路人的体形范围在[l,r],问从1走到n的可行体形有几个(2333333
思路:首先把区间离散化,然后建立一棵以区间为关键字的线段树,再把所有边都按区间加进对应的线段树节点。然后从线段树的根节点不断向下dfs,每次用按秩(即深度)合并优化并查集,不能用路径压缩,因为回溯的时候要撤销并查集操作。到了叶子结点的时候查询下1和n是否在一个集合里,是的话就加上这个区间的贡献,不是的话就继续向下dfs。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int N=1e5+10;
struct edge{ //边
int u,v,l,r;
}f[N];
int pre[N],rk[N]; //pre[]表示父节点,rk[]表示并查集的秩
int q[N<<1];
vector <P> a[N<<3]; //线段树
vector <int> t[30];
P V;
int n,m,ans;
void build(int x,int l,int r,int fl,int fr)
{
if(l==fl&&r==fr){
a[x].push_back(V);
return;
}
int mid=(l+r)>>1;
if(fr<=mid) build(x<<1,l,mid,fl,fr);
else if(fl>=mid) build(x<<1|1,mid,r,fl,fr);
else build(x<<1,l,mid,fl,mid),build(x<<1|1,mid,r,mid,fr);
}
int Find(int x)
{
return pre[x]==x?x:Find(pre[x]);
}
void join(int rt,int x,int y,int dep)
{
x=Find(x),y=Find(y);
if(x!=y){
if(rk[x]<=rk[y]){ //按秩合并
pre[x]=y,t[dep].push_back(x);
if(rk[x]==rk[y]) rk[y]++;
}
else{
pre[y]=x,t[dep].push_back(y);
}
}
}
void dfs(int x,int l,int r,int dep) //x表示节点编号,dep表示节点深度
{
t[dep].clear();
for(auto i:a[x]) join(x,i.first,i.second,dep);
if(l==r-1){
if(Find(1)==Find(n)) ans+=q[r]-q[l]; //更新贡献
for(auto i:t[dep]) pre[i]=i;
return;
}
int mid=l+r>>1;
dfs(x<<1,l,mid,dep+1);
dfs(x<<1|1,mid,r,dep+1);
for(auto i:t[dep]) pre[i]=i;
}
int main()
{
scanf("%d%d",&n,&m);
int cnt=0;
for(int i=1;i<=n;i++) pre[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&f[i].u,&f[i].v,&f[i].l,&f[i].r);
q[++cnt]=f[i].l; q[++cnt]=f[i].r+1;
}
sort(q+1,q+cnt+1); //把区间排序进行离散化
cnt=unique(q+1,q+cnt+1)-q-1;
for(int i=1;i<=m;i++){
V=P(f[i].u,f[i].v);
int p1=lower_bound(q+1,q+cnt+1,f[i].l)-q;
int p2=lower_bound(q+1,q+cnt+1,f[i].r+1)-q;
build(1,1,cnt,p1,p2); //以区间为关键字建树
}
ans=0;
dfs(1,1,cnt,1); //从根1向下dfs
printf("%d\n",ans);
return 0;
}