题目:
题意:
给定一张图,每条边有个区间【L,R】,当你的权值在这个区间时才能走这条边,问一共有多少权值可以从 1 走到 N
笔记:
可撤销并查集:
就是没有路径压缩的并查集,但为了快速找到祖宗节点,采用启发式合并的方法,每次将节点数量少的集合合并到节点多的集合上;这样合并后,从叶子节点每往上移动一个节点,总的节点数至少增加一倍,所以整颗树的高度是 logN ,那么每次找祖宗节点的复杂度也就是O(logN),这样做的好处就是方便撤销
分析:
这道题的做法就是经典的时间分治,按照L,R的值建一颗线段树,把每条边存到对应【L,R】的区间的节点上,然后遍历一遍线段树,同时将每个节点保存的边加入并查集,每次只需要判断 1,N 是否在一个集合就能判断这段区间对应的【xL,xR】是否合法,回溯的时候撤销加入的边;这道题的 L,R比较大,考虑将 L,R+1离散化后线段树的叶子节点表示一段连续的数字即可
代码:
#include <bits/stdc++.h>
#define f first
#define s second
#define pii pair<int,int>
using namespace std;
typedef long long LL;
const int maxn = 1e5+25;
int n,m,c[maxn<<1],cnt;
struct node{
int u,v,l,r;
}q[maxn];
inline int getid(int x){
return lower_bound(c+1,c+cnt,x)-c;
}
std::vector<int> v[maxn<<3];
void updata(int l,int r,int L,int R,int x,int t){
if(l > R || r < L) return ;
if(l >= L && r <= R){
v[x].push_back(t);
return ;
}
int mid = (l+r) >> 1;
updata(l,mid,L,R,x<<1,t);
updata(mid+1,r,L,R,x<<1|1,t);
}
int fa[maxn],sz[maxn];
int Find(int x){
return fa[x]==x ? x : Find(fa[x]);
}
stack<pii> s;
inline void mix(int x,int id){
int fu = Find(q[x].u);
int fv = Find(q[x].v);
if(fu != fv){
if(sz[fu] > sz[fv]){
fa[fv] = fu; sz[fu] += sz[fv];
s.push(pii(id,fv));
}
else{
fa[fu] = fv; sz[fv] += sz[fu];
s.push(pii(id,fu));
}
}
}
inline void cancal(int id){
while(!s.empty()&&s.top().f==id){
int x = s.top().s; s.pop();
sz[fa[x]] -= sz[x];
fa[x] = x;
}
}
void query(int l,int r,int x,LL &ans){
for(int i = 0;i<(int)v[x].size();++i) mix(v[x][i],x);
if(Find(1) == Find(n)){
ans += c[r+1]-c[l];
cancal(x);
return ;
}
if(l == r){
cancal(x);
return ;
}
int mid = (l+r) >> 1;
query(l,mid,x<<1,ans);
query(mid+1,r,x<<1|1,ans);
cancal(x);
}
int main(){
scanf("%d %d",&n,&m); cnt = 1;
for(int i = 0;i <= n; ++i) fa[i] = i,sz[i] = 1;
for(int i = 0;i < m; ++i){
scanf("%d %d %d %d",&q[i].u,&q[i].v,&q[i].l,&q[i].r);
c[cnt++] = q[i].l, c[cnt++] = q[i].r+1;
}
sort(c+1,c+cnt); cnt = unique(c+1,c+cnt)-c;
for(int i = 0;i < m; ++i){
int L = getid(q[i].l);
int R = getid(q[i].r+1)-1;
updata(1,cnt-2,L,R,1,i);
}
LL ans = 0;
query(1,cnt-2,1,ans);
cout << ans << '\n';
return 0;
}
300

被折叠的 条评论
为什么被折叠?



