https://ac.nowcoder.com/acm/contest/888/E
题意:每条边有一个
[
l
,
r
]
[l,r]
[l,r],表示通过的人的大小必须在此范围。求有多少种大小的人可以从1到达n。
思路:先对范围离散化,并以此为下标,建一棵线段树,结点维护某个范围区间有哪些边,通过并查集维护连通性。并查集要按秩合并,因为要保持形态。
注意不能直接维护闭区间,否则会有区间损失问题,同扫描线线段树https://blog.youkuaiyun.com/Wen_Yongqi/article/details/90181962
必须转成左闭右开区间,再维护。
综上所述:这种线段树每个结点u维护的是u到u+1范围的权值,可以理解为边,维护孤点会造成区间损失。扫描线r直接放进离散化,insert进线段树时r的位置-1,而这个线段树r+1放进离散化,insert进(r+1)的位置-1。这里-1是因为线段树的结点维护边,r+1是为了闭转开,这个问题扫描线是没有意义的(因为扫描线维护的比如[2,4]本来就是2-3和3-4,而这道题的[2,4]是2-3和3-4和4-5长度为3)。
补一个不+1的错误反例:
3 2
2 3 8 18
1 2 2 20
#include<bits/stdc++.h>
using namespace std;
const int maxn=200000+100;
#define P pair<int,int>
int n,m,p[maxn],ans,d[maxn],sz;
vector<int> nums,s[maxn*4];
struct Edge{
int u,v,l,r;
}e[maxn];
struct Tree{
vector<P> v;
}tree[maxn*4];
int pos(int x){return lower_bound(nums.begin(),nums.end(),x)+1-nums.begin() ;}
void update(int o,int l,int r,int b,int c,int u,int v)
{
if(b<=l&&r<=c)
{
tree[o].v.push_back(make_pair(u,v));
}
else
{
int m=(l+r)/2;
if(b<=m)update(o*2,l,m,b,c,u,v);
if(c>m)update(o*2+1,m+1,r,b,c,u,v);
}
}
int find(int x){return x==p[x]?x:find(p[x]);}
void Union(int o,int x,int y)
{
x=find(x),y=find(y);
if(x==y)return;
if(d[x]>d[y])swap(x,y);
p[x]=y;
if(d[x]==d[y])d[y]++;
s[o].push_back(x);
}
void solve(int o)
{
for(int i=0;i<tree[o].v.size();i++)
{
int x=tree[o].v[i].first,y=tree[o].v[i].second;
Union(o,x,y);
}
}
void del(int o)
{
for(int i=0;i<s[o].size();i++)
{
int x=s[o][i];
p[x]=x;
}
}
void dfs(int o,int l,int r)
{
solve(o);
int x=find(1),y=find(n);
if(x==y)
{
ans+=nums[r-1+1]-nums[l-1];//cout<<nums[l-1]<<" "<<nums[r-1]<<endl;
del(o);
return;
}
if(l==r)
{
del(o);
return;
}
int m=(l+r)/2;
dfs(o*2,l,m);
dfs(o*2+1,m+1,r);
del(o);
}
int main()
{
//freopen("input.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=m;i++)scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].r),nums.push_back(e[i].l),nums.push_back(e[i].r+1);
for(int i=1;i<=n;i++)p[i]=i;
sort(nums.begin(),nums.end());
nums.erase(unique(nums.begin(),nums.end()),nums.end());
sz=nums.size();
for(int i=1;i<=m;i++)update(1,1,sz,pos(e[i].l),pos(e[i].r+1)-1,e[i].u,e[i].v);
dfs(1,1,sz);
cout<<ans<<endl;
return 0;
}