E. Explorer
Explorer
题目大意
给定一个n个点m条边的无向图。每个边拥有四个属性,u,v,l,r分别表示为连接u,v两点的一条边,其只能通过体型在[l,r]的人。问从1到n有多少体型可以通过。
分析
蛮有意思的一道题目。我尽量以我的视角来说明白这道题的思路。
我们考虑,最直接的思路。我们将从1到n的所有所有路都走一边,然后走的时候直接求一个区间交。但是时间复杂度一定是过不去的了。
我们反过头看一下,我们要求的区间交,我们看看能否将所有区间铺平,将其离散化。
接下来,我们思考如何处理我们的边。一个边的影响是在某个范围内可以将u,v连接起来。
则我们的问题就转化为了,哪些区间的交可以使得我们的1和n联通。
这个并没有什么好办法,我们只能考虑暴力求解了。
我们考虑利用线段树,以离散化后的区间的坐标建树,其中维护的是当前区间内可以连接起来的端点。
这样我们直接从线段树的根节点,向下爆搜,以此来代替求区间交的过程。在这个过程中,我们维护并查集,来帮助我们完成在搜到叶节点后判断1和n是否是联通的。但是因为我们会回溯,因此我们需要维护的不是普通的并查集,而是可撤销并查集。
其中有一些细节。我们假设alls是离散化后的数组。
- 如果我们离散区间时,是以
l和r来进行的。我们考虑一下统计答案时,如果是alls[x]和alls[x+1]是[l1,l2),那我们其实直接用alls[x+1]-alls[x]即可。但是如果是alls[x]和alls[x+1]是[l1,r1],那就尴尬了,因为这个右端点其实我们是要取到的。这里我们用了很巧妙的方式,我们插入的时候直接插入[l,r+1]这样,我们就解决了那个问题。 - 另一个细节是边界问题,我们考虑一下,如果取到最后一个区间,那我们就直接+1即可。
来看看代码。
Ac_code
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
struct DSU
{
int fa[N];
int sz[N];
vector<pair<int&, int>>his_sz;
vector<pair<int&, int>>his_fa;
void init(int n) {
for (int i = 1; i <= n; i++)fa[i] = i, sz[i] = 1;
}
int find(int x) {
while (x != fa[x])x = fa[x];
return x;
}
bool same(int u, int v) {
return find(u) == find(v);
}
void merge(int u, int v) {
int x = find(u);
int y = find(v);
if (x == y) return;
if (sz[x] < sz[y]) std::swap(x, y);
his_sz.push_back({ sz[x], sz[x] });
sz[x] = sz[x] + sz[y];
his_fa.push_back({ fa[y],fa[y] });
fa[y] = x;
}
int histroy() {
return his_fa.size();
}
void roll(int h) {
while (his_fa.size() > h) {
his_fa.back().first = his_fa.back().second;
his_fa.pop_back();
his_sz.back().first = his_sz.back().second;
his_sz.pop_back();
}
}
}dsu;
struct Seg
{
int l,r;
vector<pair<int,int>> res;
}tr[N<<2];
struct Node
{
int u,v,l,r;
}p[N];
vector<int> alls;
int n,m,ans,cnt;
int get(int x)
{
return lower_bound(alls.begin(),alls.end(),x) - alls.begin() + 1;
}
void build(int u,int l,int r)
{
tr[u] = {l,r};
if(l==r) return ;
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int x,int y)
{
if(tr[u].l==l&&tr[u].r==r)
{
tr[u].res.push_back({x,y});
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(r<=mid) modify(u<<1,l,r,x,y);
else if(l>mid) modify(u<<1|1,l,r,x,y);
else modify(u<<1,l,mid,x,y),modify(u<<1|1,mid+1,r,x,y);
}
void dfs(int i,int l,int r,int res){
int cnt = res;
for(auto [x,y]:tr[i].res)
if(!dsu.same(x,y))
{
res++;
dsu.merge(x,y);
}
if(l==r){
if(dsu.find(1)==dsu.find(n))
if(r<alls.size()) ans+=alls[r]-alls[l-1];
else ans++;
dsu.roll(cnt);
return ;
}
int mid=l+r>>1;
dfs(i<<1,l,mid,res);
dfs(i<<1|1,mid+1,r,res);
dsu.roll(cnt);
}
int main()
{
ios;
cin>>n>>m;dsu.init(n);
for(int i=1;i<=m;i++)
{
int u,v,l,r;
cin>>u>>v>>l>>r;
p[i] = {u,v,l,r};
alls.push_back(l),alls.push_back(r);
}
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
cnt = alls.size();
build(1,1,cnt);
for(int i=1;i<=m;i++)
{
int l = get(p[i].l),r = get(p[i].r);
modify(1,l,r,p[i].u,p[i].v);
}
dfs(1,1,cnt,0);
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号